Browse Source

Trying a different type of sparse array

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
8999a78cff
3 changed files with 380 additions and 668 deletions
  1. 189 235
      src/anki/util/SparseArray.h
  2. 182 430
      src/anki/util/SparseArray.inl.h
  3. 9 3
      tests/util/SparseArray.cpp

+ 189 - 235
src/anki/util/SparseArray.h

@@ -17,106 +17,69 @@ namespace anki
 /// @addtogroup util_containers
 /// @addtogroup util_containers
 /// @{
 /// @{
 
 
-/// A single bucket of SparseArray elements. It's its own class in case we allow multiple buckets in the future.
-/// @internal
-template<typename TNode, PtrSize TBucketSize>
-class SparseArrayBucket
-{
-public:
-	Array<TNode*, TBucketSize> m_elements;
-};
-
-/// SparseArray node.
-/// @internal
-template<typename T>
-class SparseArrayNode
-{
-public:
-	SparseArrayNode* m_saLeft = nullptr;
-	SparseArrayNode* m_saRight = nullptr;
-	SparseArrayNode* m_saParent = nullptr; ///< Used for iterating.
-	PtrSize m_saIdx = 0; ///< The index in the sparse array.
-	T m_saValue;
-
-	template<typename... TArgs>
-	SparseArrayNode(TArgs&&... args)
-		: m_saValue(std::forward<TArgs>(args)...)
-	{
-	}
-
-	T& getSaValue()
-	{
-		return m_saValue;
-	}
-
-	const T& getSaValue() const
-	{
-		return m_saValue;
-	}
-};
-
 /// Sparse array iterator.
 /// Sparse array iterator.
-template<typename TNodePointer, typename TValuePointer, typename TValueReference, typename TSparseArrayBasePtr>
+template<typename TValuePointer, typename TValueReference, typename TSparseArrayPtr>
 class SparseArrayIterator
 class SparseArrayIterator
 {
 {
-	template<typename, typename, typename, PtrSize, PtrSize>
-	friend class SparseArrayBase;
-
-	template<typename, typename, PtrSize, PtrSize>
+	template<typename, typename>
 	friend class SparseArray;
 	friend class SparseArray;
 
 
 public:
 public:
 	/// Default constructor.
 	/// Default constructor.
 	SparseArrayIterator()
 	SparseArrayIterator()
-		: m_node(nullptr)
-		, m_array(nullptr)
+		: m_array(nullptr)
+		, m_elementIdx(MAX_U32)
 	{
 	{
 	}
 	}
 
 
 	/// Copy.
 	/// Copy.
 	SparseArrayIterator(const SparseArrayIterator& b)
 	SparseArrayIterator(const SparseArrayIterator& b)
-		: m_node(b.m_node)
-		, m_array(b.m_array)
+		: m_array(b.m_array)
+		, m_elementIdx(b.m_elementIdx)
 	{
 	{
 	}
 	}
 
 
 	/// Allow conversion from iterator to const iterator.
 	/// Allow conversion from iterator to const iterator.
-	template<typename YNodePointer, typename YValuePointer, typename YValueReference, typename YSparseArrayBasePtr>
-	SparseArrayIterator(const SparseArrayIterator<YNodePointer, YValuePointer, YValueReference, YSparseArrayBasePtr>& b)
-		: m_node(b.m_node)
-		, m_array(b.m_array)
+	template<typename YValuePointer, typename YValueReference, typename YSparseArrayPtr>
+	SparseArrayIterator(const SparseArrayIterator<YValuePointer, YValueReference, YSparseArrayPtr>& b)
+		: m_array(b.m_array)
+		, m_elementIdx(b.m_elementIdx)
 	{
 	{
 	}
 	}
 
 
-	SparseArrayIterator(TNodePointer node, TSparseArrayBasePtr arr)
-		: m_node(node)
-		, m_array(arr)
+	SparseArrayIterator(TSparseArrayPtr arr, U32 modIdx)
+		: m_array(arr)
+		, m_elementIdx(modIdx)
 	{
 	{
 		ANKI_ASSERT(arr);
 		ANKI_ASSERT(arr);
 	}
 	}
 
 
 	TValueReference operator*() const
 	TValueReference operator*() const
 	{
 	{
-		ANKI_ASSERT(m_node && m_array);
-		return m_node->getSaValue();
+		check();
+		return m_array->m_elements[m_elementIdx].m_value;
 	}
 	}
 
 
 	TValuePointer operator->() const
 	TValuePointer operator->() const
 	{
 	{
-		ANKI_ASSERT(m_node && m_array);
-		return &m_node->getSaValue();
+		check();
+		return &m_array->m_elements[m_elementIdx].m_value;
 	}
 	}
 
 
 	SparseArrayIterator& operator++()
 	SparseArrayIterator& operator++()
 	{
 	{
-		ANKI_ASSERT(m_node && m_array);
-		m_node = m_array->getNextNode(m_node);
+		check();
+		++m_elementIdx;
+		if(m_elementIdx >= m_array->m_capacity)
+		{
+			m_elementIdx = MAX_U32;
+		}
 		return *this;
 		return *this;
 	}
 	}
 
 
 	SparseArrayIterator operator++(int)
 	SparseArrayIterator operator++(int)
 	{
 	{
-		ANKI_ASSERT(m_node && m_array);
+		check();
 		SparseArrayIterator out = *this;
 		SparseArrayIterator out = *this;
 		++(*this);
 		++(*this);
 		return out;
 		return out;
@@ -124,26 +87,30 @@ public:
 
 
 	SparseArrayIterator operator+(U n) const
 	SparseArrayIterator operator+(U n) const
 	{
 	{
-		SparseArrayIterator it = *this;
-		while(n-- != 0)
+		check();
+		U32 pos = m_elementIdx + n;
+		if(pos >= m_array->m_capacity)
 		{
 		{
-			++it;
+			pos = MAX_U32;
 		}
 		}
-		return it;
+		return SparseArrayIterator(m_array, pos);
 	}
 	}
 
 
 	SparseArrayIterator& operator+=(U n)
 	SparseArrayIterator& operator+=(U n)
 	{
 	{
-		while(n-- != 0)
+		check();
+		m_elementIdx += n;
+		if(m_elementIdx >= m_array->m_capacity)
 		{
 		{
-			++(*this);
+			m_elementIdx = MAX_U32;
 		}
 		}
 		return *this;
 		return *this;
 	}
 	}
 
 
 	Bool operator==(const SparseArrayIterator& b) const
 	Bool operator==(const SparseArrayIterator& b) const
 	{
 	{
-		return m_node == b.m_node;
+		ANKI_ASSERT(m_array == b.m_array);
+		return m_elementIdx == b.m_elementIdx;
 	}
 	}
 
 
 	Bool operator!=(const SparseArrayIterator& b) const
 	Bool operator!=(const SparseArrayIterator& b) const
@@ -153,57 +120,121 @@ public:
 
 
 	operator Bool() const
 	operator Bool() const
 	{
 	{
-		return m_node != nullptr;
+		return m_elementIdx != MAX_U32;
 	}
 	}
 
 
 private:
 private:
-	TNodePointer m_node;
-	TSparseArrayBasePtr m_array;
+	TSparseArrayPtr m_array;
+	U32 m_elementIdx;
+
+	void check() const
+	{
+		ANKI_ASSERT(m_elementIdx != MAX_U32 && m_array);
+		ANKI_ASSERT(m_array->m_elements[m_elementIdx].m_alive);
+	}
 };
 };
 
 
-/// Sparse array base class.
-/// @internal
-template<typename T, typename TNode, typename TIndex = U32, PtrSize TBucketSize = 128, PtrSize TLinearProbingSize = 4>
-class SparseArrayBase
+/// Sparse array.
+/// @tparam T The type of the valut it will hold.
+template<typename T, typename TIndex = U32>
+class SparseArray
 {
 {
-	template<typename, typename, typename, typename>
+	template<typename, typename, typename>
 	friend class SparseArrayIterator;
 	friend class SparseArrayIterator;
 
 
 public:
 public:
 	// Typedefs
 	// Typedefs
-	using Self = SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>;
 	using Value = T;
 	using Value = T;
-	using Node = TNode;
+	using Iterator = SparseArrayIterator<T*, T&, SparseArray*>;
+	using ConstIterator = SparseArrayIterator<const T*, const T&, const SparseArray*>;
 	using Index = TIndex;
 	using Index = TIndex;
-	using Iterator = SparseArrayIterator<TNode*, T*, T&, Self*>;
-	using ConstIterator = SparseArrayIterator<const TNode*, const T*, const T&, const Self*>;
 
 
-	// Some constants
-	static constexpr PtrSize BUCKET_SIZE = TBucketSize;
-	static constexpr PtrSize LINEAR_PROBING_SIZE = TLinearProbingSize;
+	/// Constructor #1.
+	SparseArray(U32 initialStorageSize, U32 probeCount, F32 maxLoadFactor)
+		: m_initialStorageSize(initialStorageSize)
+		, m_probeCount(probeCount)
+		, m_maxLoadFactor(maxLoadFactor)
+	{
+		ANKI_ASSERT(initialStorageSize > 0 && isPowerOfTwo(initialStorageSize));
+		ANKI_ASSERT(probeCount > 0 && probeCount < initialStorageSize);
+		ANKI_ASSERT(maxLoadFactor > 0.0f && maxLoadFactor <= 1.0f);
+	}
+
+	/// Constructor #2.
+	SparseArray(U32 initialStorageSize, U32 probeCount)
+		: SparseArray(initialStorageSize, probeCount, 0.8f)
+	{
+	}
+
+	/// Constructor #3.
+	SparseArray(U32 initialStorageSize)
+		: SparseArray(initialStorageSize, log2(initialStorageSize))
+	{
+	}
+
+	/// Constructor #4.
+	SparseArray()
+		: SparseArray(64)
+	{
+	}
+
+	/// Non-copyable.
+	SparseArray(const SparseArray&) = delete;
+
+	/// Move constructor.
+	SparseArray(SparseArray&& b)
+	{
+		*this = std::move(b);
+	}
+
+	/// Destroy.
+	~SparseArray()
+	{
+		ANKI_ASSERT(m_elements == nullptr && "Forgot to call destroy");
+	}
+
+	/// Non-copyable.
+	SparseArray& operator=(const SparseArray&) = delete;
+
+	/// Move operator.
+	SparseArray& operator=(SparseArray&& b)
+	{
+		ANKI_ASSERT(m_elements == nullptr && "Forgot to call destroy");
+
+		m_elements = b.m_elements;
+		m_elementCount = b.m_elementCount;
+		m_capacity = b.m_capacity;
+		m_initialStorageSize = b.m_initialStorageSize;
+		m_probeCount = b.m_probeCount;
+		m_maxLoadFactor = b.m_maxLoadFactor;
+
+		b.resetMembers();
+
+		return *this;
+	}
 
 
 	/// Get begin.
 	/// Get begin.
 	Iterator getBegin()
 	Iterator getBegin()
 	{
 	{
-		return Iterator(m_bucket.m_elements[findFirstModIdx()], this);
+		return Iterator(this, findFirstAlive());
 	}
 	}
 
 
 	/// Get begin.
 	/// Get begin.
 	ConstIterator getBegin() const
 	ConstIterator getBegin() const
 	{
 	{
-		return ConstIterator(m_bucket.m_elements[findFirstModIdx()], this);
+		return ConstIterator(this, findFirstAlive());
 	}
 	}
 
 
 	/// Get end.
 	/// Get end.
 	Iterator getEnd()
 	Iterator getEnd()
 	{
 	{
-		return Iterator(nullptr, this);
+		return Iterator();
 	}
 	}
 
 
 	/// Get end.
 	/// Get end.
 	ConstIterator getEnd() const
 	ConstIterator getEnd() const
 	{
 	{
-		return ConstIterator(nullptr, this);
+		return ConstIterator();
 	}
 	}
 
 
 	/// Get begin.
 	/// Get begin.
@@ -242,189 +273,112 @@ public:
 		return m_elementCount != 0;
 		return m_elementCount != 0;
 	}
 	}
 
 
-	/// Check the validity of the array.
-	void validate() const;
+	/// Destroy the array and free its elements.
+	template<typename TAlloc>
+	void destroy(TAlloc& alloc);
 
 
-protected:
-	SparseArrayBucket<Node, BUCKET_SIZE> m_bucket;
-	U32 m_elementCount = 0;
+	/// Set a value to an index.
+	template<typename TAlloc, typename... TArgs>
+	Value& emplace(TAlloc& alloc, Index idx, TArgs&&... args);
 
 
-	/// Default constructor.
-	SparseArrayBase()
+	/// Get an iterator.
+	Iterator find(Index idx)
 	{
 	{
-		ANKI_ASSERT(isPowerOfTwo(BUCKET_SIZE));
-		zeroMemory(m_bucket);
+		return Iterator(this, findInternal(idx));
 	}
 	}
 
 
-	/// Non-copyable.
-	SparseArrayBase(const SparseArrayBase&) = delete;
-
-	/// Move constructor.
-	SparseArrayBase(SparseArrayBase&& b)
+	/// Get an iterator.
+	ConstIterator find(Index idx) const
 	{
 	{
-		*this = std::move(b);
+		return ConstIterator(this, findInternal(idx));
 	}
 	}
 
 
-	/// Destroy.
-	~SparseArrayBase()
-	{
-#if ANKI_EXTRA_CHECKS
-		zeroMemory(m_bucket);
-#endif
-	}
+	/// Remove an element.
+	template<typename TAlloc>
+	void erase(TAlloc& alloc, Iterator it);
 
 
-	/// Non-copyable.
-	SparseArrayBase& operator=(const SparseArrayBase&) = delete;
+	/// Check the validity of the array.
+	void validate() const;
 
 
-	/// Move operator.
-	SparseArrayBase& operator=(SparseArrayBase&& b)
+protected:
+	/// Element.
+	class Element
 	{
 	{
-		if(b.m_elementCount)
-		{
-			memcpy(&m_bucket[0], &b.m_bucket[0], sizeof(m_bucket));
-			m_elementCount = b.m_elementCount;
-			zeroMemory(b.m_bucket);
-			b.m_elementCount = 0;
-		}
-		else
-		{
-			zeroMemory(m_bucket);
-			m_elementCount = 0;
-		}
-
-		return *this;
-	}
+	public:
+		Value m_value;
+		Index m_idx;
+		Bool8 m_alive;
+	};
 
 
-	/// Find a place for a node in the array.
-	Node** findPlace(Index idx, Node*& parent);
-
-	/// Remove a node.
-	void remove(Iterator it);
-
-	/// Try get an node.
-	const Node* tryGetNode(Index idx) const;
+	Element* m_elements = nullptr;
+	U32 m_elementCount = 0;
+	U32 m_capacity = 0;
 
 
-	/// For iterating.
-	const Node* getNextNode(const Node* const node) const
-	{
-		return getNextNodeInternal(node);
-	}
+	U32 m_initialStorageSize = 0;
+	U32 m_probeCount = 0;
+	F32 m_maxLoadFactor = 0.0;
 
 
-	/// For iterating.
-	Node* getNextNode(const Node* node)
+	Index mod(const Index idx) const
 	{
 	{
-		const Node* out = getNextNodeInternal(node);
-		return const_cast<Node*>(out);
+		return mod(idx, m_capacity);
 	}
 	}
 
 
-private:
-	static Index mod(const Index idx)
+	static Index mod(const Index idx, U32 capacity)
 	{
 	{
-		return idx & (BUCKET_SIZE - 1);
+		ANKI_ASSERT(capacity > 0);
+		ANKI_ASSERT(isPowerOfTwo(capacity));
+		return idx & (capacity - 1);
 	}
 	}
 
 
-	static void insertToTree(Node* const root, Node* node);
-
-	/// Remove a node from the tree.
-	/// @return The new root node.
-	static Node* removeFromTree(Node* root, Node* del);
-
-	/// See validate().
-	void validateInternal(Index modIdx, const Node* node, PtrSize& count) const;
-
-	/// For iterating.
-	Index findFirstModIdx() const;
-
-	const Node* getNextNodeInternal(const Node* node) const;
-};
-
-/// Sparse array.
-/// @tparam T The type of the valut it will hold.
-/// @tparam TIndex The type of the index. It should be U32 or U64.
-/// @tparam TBucketeSize The number of the preallocated size.
-/// @tparam TLinearProbingSize The number of positions that will be linearly probed when inserting.
-template<typename T, typename TIndex = U32, PtrSize TBucketSize = 128, PtrSize TLinearProbingSize = 4>
-class SparseArray : public SparseArrayBase<T, SparseArrayNode<T>, TIndex, TBucketSize, TLinearProbingSize>
-{
-public:
-	using Base = SparseArrayBase<T, SparseArrayNode<T>, TIndex, TBucketSize, TLinearProbingSize>;
-	using Node = typename Base::Node;
-	using Value = typename Base::Value;
-	using Index = typename Base::Index;
-	using Iterator = typename Base::Iterator;
-	using ConstIterator = typename Base::ConstIterator;
-
-	using Base::BUCKET_SIZE;
-	using Base::LINEAR_PROBING_SIZE;
-
-	SparseArray()
+	F32 calcLoadFactor() const
 	{
 	{
+		ANKI_ASSERT(m_elementCount <= m_capacity);
+		ANKI_ASSERT(m_capacity > 0);
+		return F32(m_elementCount) / m_capacity;
 	}
 	}
 
 
-	/// Non-copyable.
-	SparseArray(const SparseArray&) = delete;
+	/// Grow the storage and re-insert.
+	template<typename TAlloc>
+	void grow(TAlloc& alloc);
 
 
-	/// Move constructor.
-	SparseArray(SparseArray&& b)
+	/// Compute the distance between a desired position and the current one. This method does a trick with capacity to
+	/// account wrapped positions.
+	Index distanceFromDesired(const Index crntPos, const Index desiredPos) const
 	{
 	{
-		*this = std::move(b);
+		return mod(crntPos + m_capacity - desiredPos);
 	}
 	}
 
 
-	/// Destroy. It will do nothing.
-	~SparseArray()
+	Index findFirstAlive() const
 	{
 	{
-		ANKI_ASSERT(m_elementCount == 0 && "Forgot to call destroy");
-	}
-
-	/// Non-copyable.
-	SparseArray& operator=(const SparseArray&) = delete;
-
-	/// Move operator.
-	SparseArray& operator=(SparseArray&& b)
-	{
-		ANKI_ASSERT(m_elementCount == 0 && "Forgot to destroy");
-		static_cast<Base&>(*this) = std::move(static_cast<Base&>(b));
-		return *this;
-	}
-
-	/// Destroy the array and free its elements.
-	template<typename TAlloc>
-	void destroy(TAlloc& alloc);
+		if(m_elementCount == 0)
+		{
+			return MAX_U32;
+		}
 
 
-	/// Set a value to an index.
-	template<typename TAlloc, typename... TArgs>
-	Iterator setAt(TAlloc& alloc, Index idx, TArgs&&... args);
+		for(Index i = 0; i < m_capacity; ++i)
+		{
+			if(m_elements[i].m_alive)
+			{
+				return i;
+			}
+		}
 
 
-	/// Get an iterator.
-	Iterator getAt(Index idx)
-	{
-		const Node* node = Base::tryGetNode(idx);
-		return Iterator(const_cast<Node*>(node), this);
+		ANKI_ASSERT(0);
+		return MAX_U32;
 	}
 	}
 
 
-	/// Get an iterator.
-	ConstIterator getAt(Index idx) const
-	{
-		const Node* node = Base::tryGetNode(idx);
-		return ConstIterator(node, this);
-	}
+	/// Find an element and return its position inside m_elements.
+	Index findInternal(Index idx) const;
 
 
-	/// Remove an element.
-	template<typename TAlloc>
-	void erase(TAlloc& alloc, Iterator it)
+	void resetMembers()
 	{
 	{
-		Base::remove(it);
-		alloc.deleteInstance(it.m_node);
-		ANKI_ASSERT(Base::m_elementCount > 0);
-		--m_elementCount;
+		m_elements = nullptr;
+		m_elementCount = 0;
+		m_capacity = 0;
+		m_initialStorageSize = 0;
+		m_probeCount = 0;
+		m_maxLoadFactor = 0;
 	}
 	}
-
-private:
-	using Base::m_elementCount;
-	using Base::m_bucket;
-
-	template<typename TAlloc>
-	void destroyInternal(TAlloc& alloc, Node* const root);
 };
 };
 /// @}
 /// @}
 
 

+ 182 - 430
src/anki/util/SparseArray.inl.h

@@ -8,533 +8,285 @@
 namespace anki
 namespace anki
 {
 {
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-TNode** SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::findPlace(Index idx, Node*& parent)
+template<typename T, typename TIndex>
+template<typename TAlloc>
+void SparseArray<T, TIndex>::destroy(TAlloc& alloc)
 {
 {
-	parent = nullptr;
-	const Index modIdx = mod(idx);
-
-	if(m_bucket.m_elements[modIdx] == nullptr || m_bucket.m_elements[modIdx]->m_saIdx == idx)
-	{
-		// We are lucky, found empty spot or something to replace
-		return &m_bucket.m_elements[modIdx];
-	}
-
-	// Do linear probing
-	Index probingIdx = modIdx + 1;
-	while(probingIdx - modIdx < LINEAR_PROBING_SIZE && probingIdx < BUCKET_SIZE)
-	{
-		if(m_bucket.m_elements[probingIdx] == nullptr || m_bucket.m_elements[probingIdx]->m_saIdx == idx)
-		{
-			return &m_bucket.m_elements[probingIdx];
-		}
-
-		++probingIdx;
-	}
-
-	// Check if we can evict a node. This will happen if that node is from linear probing
-	const Index otherModIdx = mod(m_bucket.m_elements[modIdx]->m_saIdx);
-	if(otherModIdx != modIdx)
-	{
-		ANKI_ASSERT(m_bucket.m_elements[modIdx]->m_saLeft == nullptr
-			&& m_bucket.m_elements[modIdx]->m_saRight == nullptr
-			&& m_bucket.m_elements[modIdx]->m_saParent == nullptr
-			&& "Can't be a tree");
-
-		// Do a hack. Chage the other node's idx
-		Node* const otherNode = m_bucket.m_elements[modIdx];
-		const Index otherNodeIdx = otherNode->m_saIdx;
-		otherNode->m_saIdx = idx;
-
-		// ...and try to find a new place for it. If we don't do the trick above findPlace will return the same place
-		Node* parent;
-		Node** newPlace = findPlace(otherNodeIdx, parent);
-		ANKI_ASSERT(*newPlace != m_bucket.m_elements[modIdx]);
-
-		// ...point the other node to the new place and restore it
-		*newPlace = otherNode;
-		otherNode->m_saIdx = otherNodeIdx;
-		otherNode->m_saParent = parent;
-
-		// ...now the modIdx place is free for out node
-		m_bucket.m_elements[modIdx] = nullptr;
-		return &m_bucket.m_elements[modIdx];
-	}
-
-	// Last thing we can do, need to append to a tree
-	ANKI_ASSERT(m_bucket.m_elements[modIdx]);
-	Node* it = m_bucket.m_elements[modIdx];
-	while(true)
+	if(m_elements)
 	{
 	{
-		if(idx > it->m_saIdx)
+		for(Index i = 0; i < m_capacity; ++i)
 		{
 		{
-			// Go to right
-			Node* right = it->m_saRight;
-			if(right)
+			if(m_elements[i].m_alive)
 			{
 			{
-				it = right;
-			}
-			else
-			{
-				parent = it;
-				return &it->m_saRight;
+				m_elements[i].m_value.~Value();
 			}
 			}
 		}
 		}
-		else if(idx < it->m_saIdx)
-		{
-			// Go to left
-			Node* left = it->m_saLeft;
-			if(left)
-			{
-				it = left;
-			}
-			else
-			{
-				parent = it;
-				return &it->m_saLeft;
-			}
-		}
-		else
-		{
-			// Equal
-			parent = it->m_saParent;
 
 
-			if(parent)
-			{
-				if(parent->m_saLeft == it)
-				{
-					return &parent->m_saLeft;
-				}
-				else
-				{
-					ANKI_ASSERT(parent->m_saRight == it);
-					return &parent->m_saRight;
-				}
-			}
-			else
-			{
-				return &m_bucket.m_elements[modIdx];
-			}
-		}
+		alloc.deallocate(m_elements, m_capacity);
 	}
 	}
 
 
-	ANKI_ASSERT(!!"Shouldn't reach that");
-	return nullptr;
+	resetMembers();
 }
 }
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-const TNode* SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::tryGetNode(Index idx) const
+template<typename T, typename TIndex>
+template<typename TAlloc, typename... TArgs>
+T& SparseArray<T, TIndex>::emplace(TAlloc& alloc, Index idx, TArgs&&... args)
 {
 {
-	const Index modIdx = mod(idx);
-
-	if(m_bucket.m_elements[modIdx] && mod(m_bucket.m_elements[modIdx]->m_saIdx) == modIdx)
-	{
-		// Walk the tree
-		const Node* it = m_bucket.m_elements[modIdx];
-		do
-		{
-			if(it->m_saIdx == idx)
-			{
-				return it;
-			}
-			else if(idx > it->m_saIdx)
-			{
-				it = it->m_saRight;
-			}
-			else
-			{
-				it = it->m_saLeft;
-			}
-		} while(it);
-	}
-
-	// Search for linear probing
-	const Index endIdx = min(BUCKET_SIZE, modIdx + LINEAR_PROBING_SIZE);
-	for(Index i = modIdx; i < endIdx; ++i)
+	if(m_capacity == 0 || calcLoadFactor() > m_maxLoadFactor)
 	{
 	{
-		const Node* const node = m_bucket.m_elements[i];
-		if(node && node->m_saIdx == idx)
-		{
-			return node;
-		}
+		grow(alloc);
 	}
 	}
 
 
-	return nullptr;
-}
+	Value tmp(std::forward<TArgs>(args)...);
+	Index desiredPos = mod(idx);
+	Index endPos = mod(desiredPos + m_probeCount - 1);
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-void SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::insertToTree(Node* const root, Node* node)
-{
-	ANKI_ASSERT(root && node);
-	ANKI_ASSERT(root != node);
-	ANKI_ASSERT(mod(root->m_saIdx) == mod(node->m_saIdx) && "Should belong to the same tree");
-
-	const Index nodeIdx = node->m_saIdx;
-	Node* it = root;
-	Bool done = false;
-	do
-	{
-		const Index idx = it->m_saIdx;
-		if(nodeIdx > idx)
-		{
-			// Go to right
-			Node* const right = it->m_saRight;
-			if(right)
-			{
-				it = right;
-			}
-			else
-			{
-				node->m_saParent = it;
-				it->m_saRight = node;
-				done = true;
-			}
-		}
-		else
-		{
-			// Go to left
-			ANKI_ASSERT(idx != nodeIdx && "Can't do that");
-			Node* const left = it->m_saLeft;
-			if(left)
-			{
-				it = left;
-			}
-			else
-			{
-				node->m_saParent = it;
-				it->m_saLeft = node;
-				done = true;
-			}
-		}
-	} while(!done);
-}
-
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-TNode* SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::removeFromTree(Node* root, Node* del)
-{
-	ANKI_ASSERT(root && del);
-	Node* const parent = del->m_saParent;
-	Node* const left = del->m_saLeft;
-	Node* const right = del->m_saRight;
-
-	if(parent)
+	while(true)
 	{
 	{
-		// If it has a parent then remove the connection to the parent and insert left and right like regular nodes
+		Index pos = desiredPos;
 
 
-		if(parent->m_saLeft == del)
-		{
-			parent->m_saLeft = nullptr;
-		}
-		else
+		while(pos != endPos)
 		{
 		{
-			ANKI_ASSERT(parent->m_saRight == del);
-			parent->m_saRight = nullptr;
-		}
+			Element& el = m_elements[pos];
 
 
-		if(left)
-		{
-			ANKI_ASSERT(left->m_saParent == del);
-			left->m_saParent = nullptr;
-			insertToTree(root, left);
-		}
+			if(!el.m_alive)
+			{
+				// Empty slot was found, construct in-place
 
 
-		if(right)
-		{
-			ANKI_ASSERT(right->m_saParent == del);
-			right->m_saParent = nullptr;
-			insertToTree(root, right);
-		}
-	}
-	else
-	{
-		// It's the root node. Make arbitrarily the left root and add the right
+				el.m_alive = true;
+				el.m_idx = idx;
+				::new(&el.m_value) Value(std::move(tmp));
 
 
-		ANKI_ASSERT(del == root && "It must be the root");
+				++m_elementCount;
+				return el.m_value;
+			}
+			else if(el.m_idx == idx)
+			{
+				// Same index was found, replace
 
 
-		if(left)
-		{
-			left->m_saParent = nullptr;
-			root = left;
+				el.m_idx = idx;
+				el.m_value.~Value();
+				::new(&el.m_value) Value(std::move(tmp));
 
 
-			if(right)
-			{
-				right->m_saParent = nullptr;
-				insertToTree(root, right);
+				return el.m_value;
 			}
 			}
-		}
-		else
-		{
-			if(right)
+
+			// Do the robin-hood
+			const Index otherDesiredPos = mod(el.m_idx);
+			if(distanceFromDesired(pos, otherDesiredPos) < distanceFromDesired(pos, desiredPos))
 			{
 			{
-				right->m_saParent = nullptr;
+				// Swap
+				std::swap(tmp, el.m_value);
+				std::swap(idx, el.m_idx);
+				desiredPos = otherDesiredPos;
+				endPos = mod(desiredPos + m_probeCount - 1);
 			}
 			}
 
 
-			root = right;
+			pos = mod(pos + 1u);
 		}
 		}
+
+		// Didn't found an empty place, need to grow and try again
+		grow(alloc);
 	}
 	}
 
 
-	return root;
+	ANKI_ASSERT(0);
+	return m_elements[0].m_value;
 }
 }
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-void SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::remove(Iterator it)
+template<typename T, typename TIndex>
+template<typename TAlloc>
+void SparseArray<T, TIndex>::erase(TAlloc& alloc, Iterator it)
 {
 {
-	ANKI_ASSERT(it.m_node && it.m_array);
 	ANKI_ASSERT(it.m_array == this);
 	ANKI_ASSERT(it.m_array == this);
+	ANKI_ASSERT(it.m_elementIdx != MAX_U32);
+	ANKI_ASSERT(m_elementCount > 0);
+
+	(void)alloc;
 
 
-	Node* const node = it.m_node;
-	Node* const parent = node->m_saParent;
-	Node* const left = node->m_saLeft;
-	Node* const right = node->m_saRight;
+	const Index pos = it.m_elementIdx;
+	ANKI_ASSERT(pos < m_capacity);
+	ANKI_ASSERT(m_elements[pos].m_alive);
 
 
-	const Index modIdx = mod(node->m_saIdx);
+	// Delete the element in the given pos
+	m_elements[pos].m_value.~Value();
+	m_elements[pos].m_alive = false;
+	--m_elementCount;
 
 
-	if(parent || left || right)
+	// Shift elements
+	Index nextPos = pos;
+	while(true)
 	{
 	{
-		// In a tree, remove
+		const Index crntPos = nextPos;
+		nextPos = mod(nextPos + 1);
 
 
-		Node* root = m_bucket.m_elements[modIdx];
-		ANKI_ASSERT(root);
-		root = removeFromTree(root, node);
+		Element& nextEl = m_elements[nextPos];
 
 
-		m_bucket.m_elements[modIdx] = root;
-	}
-	else
-	{
-		// Not yet a tree, remove it from the bucket
+		if(!nextEl.m_alive)
+		{
+			// On gaps, stop
+			break;
+		}
 
 
-		const Index endModIdx = min(modIdx + LINEAR_PROBING_SIZE, BUCKET_SIZE);
-		Index i;
-		for(i = modIdx; i < endModIdx; ++i)
+		const Index nextDesiredPos = mod(nextEl.m_idx);
+		if(nextDesiredPos == nextPos)
 		{
 		{
-			if(m_bucket.m_elements[i] == node)
-			{
-				m_bucket.m_elements[i] = nullptr;
-				break;
-			}
+			// The element is where it want's to be, stop
+			break;
 		}
 		}
 
 
-		ANKI_ASSERT(i < endModIdx && "Node not found");
+		// Shift left
+		Element& crntEl = m_elements[crntPos];
+		crntEl = std::move(nextEl);
+		crntEl.m_idx = nextEl.m_value;
+		crntEl.m_alive = true;
+		nextEl.m_alive = false;
 	}
 	}
-}
-
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-const TNode* SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::getNextNodeInternal(
-	const Node* node) const
-{
-	ANKI_ASSERT(node);
 
 
-	if(node->m_saLeft)
+	// If you erased everything destroy the storage
+	if(m_elementCount == 0)
 	{
 	{
-		return node->m_saLeft;
+		destroy(alloc);
 	}
 	}
+}
 
 
-	if(node->m_saRight)
+template<typename T, typename TIndex>
+template<typename TAlloc>
+void SparseArray<T, TIndex>::grow(TAlloc& alloc)
+{
+	if(m_capacity == 0)
 	{
 	{
-		return node->m_saRight;
+		ANKI_ASSERT(m_elementCount == 0);
+		m_capacity = m_initialStorageSize;
+		m_elements =
+			static_cast<Element*>(alloc.getMemoryPool().allocate(m_capacity * sizeof(Element), alignof(Element)));
+		memset(m_elements, 0, m_capacity * sizeof(Element));
+		return;
 	}
 	}
 
 
-	// Node without children but with a parent, move up the tree
-	const Node* out = nullptr;
-	if(node->m_saParent)
-	{
-		const Node* prevNode = node;
-		out = node->m_saParent;
-		do
-		{
-			if(out->m_saRight && out->m_saRight != prevNode)
-			{
-				return out->m_saRight;
-			}
-
-			prevNode = out;
-			out = out->m_saParent;
-		} while(out);
-
-		// Apparently the node is the rightmost leaf
-		node = prevNode;
-		ANKI_ASSERT(node);
-		ANKI_ASSERT(m_bucket.m_elements[mod(node->m_saIdx)] == node);
-	}
+	// Allocate new storage
+	const PtrSize newCapacity = m_capacity * 2;
+	Element* const newStorage =
+		static_cast<Element*>(alloc.getMemoryPool().allocate(m_capacity * sizeof(Element), alignof(Element)));
+	memset(m_elements, 0, newCapacity * sizeof(Element));
 
 
-	// Base of the tree, move to the next bucket element
+	// Find from where we start
+	Index startPos = ~Index(0);
+	for(U i = 0; i < m_capacity; ++i)
 	{
 	{
-		const Index modIdx = mod(node->m_saIdx);
-
-		// Find where the node is
-		Index i = modIdx;
-		do
-		{
-		} while(i < BUCKET_SIZE && node != m_bucket.m_elements[i++]);
-
-		ANKI_ASSERT(i <= BUCKET_SIZE);
-
-		// Now move to the next
-		for(; i < BUCKET_SIZE; ++i)
+		if(m_elements[i].m_alive)
 		{
 		{
-			if(m_bucket.m_elements[i])
+			const Index desiredPos = mod(m_elements[i].m_idx);
+			if(desiredPos <= i)
 			{
 			{
-				return m_bucket.m_elements[i];
+				startPos = i;
+				break;
 			}
 			}
 		}
 		}
 	}
 	}
+	ANKI_ASSERT(startPos != ~Index(0));
 
 
-	return nullptr;
-}
-
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-void SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::validate() const
-{
-	PtrSize count = 0;
-
-	for(Index i = 0; i < BUCKET_SIZE; ++i)
+	// Start re-inserting
+	U count = m_elementCount;
+	Index posOfOld = startPos;
+	Index posOfNew = 0;
+	while(count--)
 	{
 	{
-		const Node* it = m_bucket.m_elements[i];
-		if(it)
+		if(m_elements[posOfOld].m_alive)
 		{
 		{
-			ANKI_ASSERT(it->m_saParent == nullptr);
+			Element& el = m_elements[posOfOld];
+			const Index desiredPos = mod(el.m_idx, newCapacity);
 
 
-			if(it->m_saLeft || it->m_saRight)
+			if(posOfNew < desiredPos)
 			{
 			{
-				// It's a tree
-
-				validateInternal(i, it, count);
+				posOfNew = desiredPos;
 			}
 			}
-			else
-			{
-				// Check if it's linear probed
 
 
-				const Index modIdx = mod(it->m_saIdx);
-				if(modIdx != i)
-				{
-					ANKI_ASSERT(i > modIdx);
-					ANKI_ASSERT(i - modIdx < LINEAR_PROBING_SIZE);
-				}
-
-				++count;
-			}
+			std::swap(newStorage[posOfOld].m_value, el.m_value);
+			newStorage[posOfOld].m_idx = el.m_idx;
+			newStorage[posOfOld].m_alive = true;
 		}
 		}
+
+		// Advance
+		posOfNew = mod(posOfNew + 1, newCapacity);
+		posOfOld = mod(posOfOld + 1);
 	}
 	}
 
 
-	ANKI_ASSERT(count == m_elementCount);
+	// Finalize
+	m_capacity = newCapacity;
+	m_elements = newStorage;
 }
 }
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-void SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::validateInternal(
-	Index modIdx, const Node* node, PtrSize& count) const
+template<typename T, typename TIndex>
+void SparseArray<T, TIndex>::validate() const
 {
 {
-	ANKI_ASSERT(node);
-	ANKI_ASSERT(mod(node->m_saIdx) == modIdx);
-
-	++count;
-
-	const Node* parent = node->m_saParent;
-	(void)parent;
-	const Node* left = node->m_saLeft;
-	const Node* right = node->m_saRight;
-
-	if(left)
+	if(m_capacity == 0)
 	{
 	{
-		ANKI_ASSERT(left->m_saParent == node);
-		validateInternal(modIdx, left, count);
+		return;
 	}
 	}
 
 
-	if(right)
-	{
-		ANKI_ASSERT(right->m_saParent == node);
-		validateInternal(modIdx, right, count);
-	}
-}
+	ANKI_ASSERT(m_elementCount < m_capacity);
 
 
-template<typename T, typename TNode, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-TIndex SparseArrayBase<T, TNode, TIndex, TBucketSize, TLinearProbingSize>::findFirstModIdx() const
-{
-	for(Index i = 0; i < BUCKET_SIZE; i += 2)
+	// Find from where we start
+	Index startPos = ~Index(0);
+	for(U i = 0; i < m_capacity; ++i)
 	{
 	{
-		if(m_bucket.m_elements[i])
-		{
-			return i;
-		}
-		else if(m_bucket.m_elements[i + 1])
+		if(m_elements[i].m_alive)
 		{
 		{
-			return i + 1;
+			const Index desiredPos = mod(m_elements[i].m_idx);
+			if(desiredPos <= i)
+			{
+				startPos = i;
+				break;
+			}
 		}
 		}
 	}
 	}
 
 
-	return 0;
-}
+	// Start iterating
+	U elementCount = 0;
+	const Index endPos = mod(startPos + m_capacity - 1);
+	Index pos = startPos;
+	while(pos != endPos)
+	{
+		if(m_elements[pos].m_alive)
+		{
+			if(elementCount > 0)
+			{
+				// There is a prev, check the distances
+				const Index prevPos = mod(pos + m_capacity - 1);
 
 
-template<typename T, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-template<typename TAlloc>
-void SparseArray<T, TIndex, TBucketSize, TLinearProbingSize>::destroyInternal(TAlloc& alloc, Node* const node)
-{
-	ANKI_ASSERT(node);
+				ANKI_ASSERT(distanceFromDesired(pos, mod(m_elements[prevPos].m_idx))
+					<= distanceFromDesired(pos, mod(m_elements[pos].m_idx)));
 
 
-	Node* const left = node->m_saLeft;
-	Node* const right = node->m_saRight;
+				ANKI_ASSERT(distanceFromDesired(pos, mod(m_elements[pos].m_idx)) < m_probeCount);
+			}
 
 
-	alloc.deleteInstance(node);
+			++elementCount;
+		}
 
 
-	if(left)
-	{
-		destroyInternal(alloc, left);
+		pos = mod(pos + 1);
 	}
 	}
 
 
-	if(right)
-	{
-		destroyInternal(alloc, right);
-	}
+	ANKI_ASSERT(m_elementCount == elementCount);
 }
 }
 
 
-template<typename T, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-template<typename TAlloc>
-void SparseArray<T, TIndex, TBucketSize, TLinearProbingSize>::destroy(TAlloc& alloc)
+template<typename T, typename TIndex>
+TIndex SparseArray<T, TIndex>::findInternal(Index idx) const
 {
 {
-	for(Index i = 0; i < BUCKET_SIZE; ++i)
+	ANKI_ASSERT(m_elementCount > 0);
+	const Index desiredPos = mod(idx);
+	const Index endPos = mod(desiredPos + m_probeCount - 1);
+	Index pos = desiredPos;
+	while(pos != endPos)
 	{
 	{
-		// Destroy the tree
-
-		Node* root = m_bucket.m_elements[i];
-		if(root)
+		if(m_elements[pos].m_alive && m_elements[pos].m_idx == idx)
 		{
 		{
-			destroyInternal(alloc, root);
-			m_bucket.m_elements[i] = nullptr;
+			return pos;
 		}
 		}
-	}
-
-	m_elementCount = 0;
-}
-
-template<typename T, typename TIndex, PtrSize TBucketSize, PtrSize TLinearProbingSize>
-template<typename TAlloc, typename... TArgs>
-typename SparseArray<T, TIndex, TBucketSize, TLinearProbingSize>::Iterator
-SparseArray<T, TIndex, TBucketSize, TLinearProbingSize>::setAt(TAlloc& alloc, Index idx, TArgs&&... args)
-{
-	Node* parent;
-	Node** place = Base::findPlace(idx, parent);
-	ANKI_ASSERT(place);
-
-	if(*place)
-	{
-		// Node exists, recycle
-
-		Node* const n = *place;
-		n->m_saValue.~Value();
-		::new(&n->m_saValue) Value(std::forward<TArgs>(args)...);
-	}
-	else
-	{
-		// Node doesn't exit,
-
-		Node* newNode = alloc.template newInstance<Node>(std::forward<TArgs>(args)...);
-		newNode->m_saIdx = idx;
-		*place = newNode;
-		newNode->m_saParent = parent;
 
 
-		++Base::m_elementCount;
+		pos = mod(pos + 1);
 	}
 	}
 
 
-	return Iterator(*place, this);
+	return MAX_U32;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 9 - 3
tests/util/SparseArray.cpp

@@ -17,12 +17,14 @@ ANKI_TEST(Util, SparseArray)
 	{
 	{
 		SparseArray<PtrSize> arr;
 		SparseArray<PtrSize> arr;
 
 
-		arr.setAt(alloc, 1000, 123);
-		auto it = arr.setAt(alloc, 1000, 124);
-		ANKI_TEST_EXPECT_EQ(*arr.getAt(1000), 124);
+		arr.emplace(alloc, 1000, 123);
+		arr.emplace(alloc, 1000, 124);
+		auto it = arr.find(1000);
+		ANKI_TEST_EXPECT_EQ(*it, 124);
 		arr.erase(alloc, it);
 		arr.erase(alloc, it);
 	}
 	}
 
 
+#if 0
 	// Check destroy
 	// Check destroy
 	{
 	{
 		SparseArray<PtrSize> arr;
 		SparseArray<PtrSize> arr;
@@ -167,8 +169,10 @@ ANKI_TEST(Util, SparseArray)
 
 
 		arr.destroy(alloc);
 		arr.destroy(alloc);
 	}
 	}
+#endif
 }
 }
 
 
+#if 0
 static PtrSize akAllocSize = 0;
 static PtrSize akAllocSize = 0;
 static ANKI_DONT_INLINE void* allocAlignedAk(void* userData, void* ptr, PtrSize size, PtrSize alignment)
 static ANKI_DONT_INLINE void* allocAlignedAk(void* userData, void* ptr, PtrSize size, PtrSize alignment)
 {
 {
@@ -330,3 +334,5 @@ ANKI_TEST(Util, SparseArrayBench)
 
 
 	akMap.destroy(allocAk);
 	akMap.destroy(allocAk);
 }
 }
+
+#endif