Bläddra i källkod

Util: HashMap is now using the SparseArray

Panagiotis Christopoulos Charitos 8 år sedan
förälder
incheckning
48b931c541

+ 1 - 1
src/anki/gr/GrObjectCache.h

@@ -73,7 +73,7 @@ inline GrObjectPtr<T> GrObjectCache::newInstance(const TArg& arg, U64 overrideHa
 	if(ptr == nullptr)
 	{
 		auto tptr = m_gr->template newInstanceCached<T>(hash, this, arg);
-		m_map.pushBack(m_gr->getAllocator(), hash, tptr.get());
+		m_map.emplace(m_gr->getAllocator(), hash, tptr.get());
 		return tptr;
 	}
 	else

+ 1 - 1
src/anki/gr/RenderGraph.cpp

@@ -130,7 +130,7 @@ TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf)
 	{
 		// Didn't found the entry, create a new one
 
-		auto it2 = m_renderTargetCache.emplaceBack(getAllocator(), initInf);
+		auto it2 = m_renderTargetCache.emplace(getAllocator(), initInf);
 		entry = &(*it2);
 	}
 	else

+ 2 - 2
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -204,7 +204,7 @@ Error DSThreadAllocator::newSet(
 			m_list.erase(set);
 
 			m_list.pushBack(set);
-			m_hashmap.pushBack(m_layoutEntry->m_factory->m_alloc, hash, set);
+			m_hashmap.emplace(m_layoutEntry->m_factory->m_alloc, hash, set);
 
 			out = set;
 			break;
@@ -239,7 +239,7 @@ Error DSThreadAllocator::newSet(
 		out = m_layoutEntry->m_factory->m_alloc.newInstance<DS>();
 		out->m_handle = handle;
 
-		m_hashmap.pushBack(m_layoutEntry->m_factory->m_alloc, hash, out);
+		m_hashmap.emplace(m_layoutEntry->m_factory->m_alloc, hash, out);
 		m_list.pushBack(out);
 	}
 

+ 1 - 1
src/anki/gr/vulkan/FramebufferImpl.cpp

@@ -321,7 +321,7 @@ VkRenderPass FramebufferImpl::getRenderPassHandle(
 
 			ANKI_VK_CHECKF(vkCreateRenderPass(getDevice(), &ci, nullptr, &out));
 
-			m_rpasses.pushBack(getAllocator(), hash, out);
+			m_rpasses.emplace(getAllocator(), hash, out);
 		}
 	}
 	else

+ 1 - 1
src/anki/gr/vulkan/Pipeline.cpp

@@ -434,7 +434,7 @@ void PipelineFactory::newPipeline(PipelineStateTracker& state, Pipeline& ppline,
 		ANKI_TRACE_STOP_EVENT(VK_PIPELINE_CREATE);
 		ANKI_TRACE_INC_COUNTER(VK_PIPELINE_CREATE, 1);
 
-		m_pplines.pushBack(m_alloc, hash, pp);
+		m_pplines.emplace(m_alloc, hash, pp);
 		ppline.m_handle = pp.m_handle;
 	}
 }

+ 1 - 1
src/anki/gr/vulkan/PipelineLayout.cpp

@@ -57,7 +57,7 @@ Error PipelineLayoutFactory::newPipelineLayout(
 		VkPipelineLayout pplineLayHandle;
 		ANKI_VK_CHECK(vkCreatePipelineLayout(m_dev, &ci, nullptr, &pplineLayHandle));
 
-		m_layouts.pushBack(m_alloc, hash, pplineLayHandle);
+		m_layouts.emplace(m_alloc, hash, pplineLayHandle);
 
 		layout.m_handle = pplineLayHandle;
 	}

+ 1 - 1
src/anki/gr/vulkan/TextureImpl.cpp

@@ -694,7 +694,7 @@ VkImageView TextureImpl::getOrCreateView(const VkImageViewCreateInfo& ci)
 	{
 		VkImageView view = VK_NULL_HANDLE;
 		ANKI_VK_CHECKF(vkCreateImageView(getDevice(), &ci, nullptr, &view));
-		m_viewsMap.pushBack(getAllocator(), ci, view);
+		m_viewsMap.emplace(getAllocator(), ci, view);
 
 		return view;
 	}

+ 2 - 2
src/anki/gr/vulkan/TextureImpl.inl.h

@@ -183,7 +183,7 @@ inline void TextureImpl::updateTracker(
 		// Not found
 		TextureUsageState state;
 		updateUsageState(surfOrVol, usage, tracker.m_alloc, state);
-		tracker.m_map.emplaceBack(tracker.m_alloc, m_uuid, std::move(state));
+		tracker.m_map.emplace(tracker.m_alloc, m_uuid, std::move(state));
 	}
 }
 
@@ -203,7 +203,7 @@ inline void TextureImpl::updateTracker(TextureUsageBit usage, TextureUsageTracke
 		// Not found
 		TextureUsageState state;
 		updateUsageState(usage, tracker.m_alloc, state);
-		tracker.m_map.emplaceBack(tracker.m_alloc, m_uuid, std::move(state));
+		tracker.m_map.emplace(tracker.m_alloc, m_uuid, std::move(state));
 	}
 }
 

+ 1 - 1
src/anki/renderer/Indirect.cpp

@@ -373,7 +373,7 @@ void Indirect::prepareProbes(
 		// Update cache map
 		if(!cacheEntryFoundInCache)
 		{
-			m_probeUuidToCacheEntryIdx.pushBack(getAllocator(), probe.m_uuid, cacheEntryIdx);
+			m_probeUuidToCacheEntryIdx.emplace(getAllocator(), probe.m_uuid, cacheEntryIdx);
 		}
 
 		// Don't gather renderables next frame

+ 1 - 1
src/anki/renderer/ShadowMapping.cpp

@@ -627,7 +627,7 @@ Bool ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 			{
 				TileKey key{lightUuid, faceIndices[i]};
 				ANKI_ASSERT(m_lightUuidToTileIdx.find(key) == m_lightUuidToTileIdx.getEnd());
-				m_lightUuidToTileIdx.pushBack(getAllocator(), key, tileIndices[i]);
+				m_lightUuidToTileIdx.emplace(getAllocator(), key, tileIndices[i]);
 			}
 		}
 	}

+ 1 - 1
src/anki/resource/ShaderProgramResource.cpp

@@ -828,7 +828,7 @@ void ShaderProgramResource::getOrCreateVariant(WeakArray<const ShaderProgramReso
 		ShaderProgramResourceVariant* v = getAllocator().newInstance<ShaderProgramResourceVariant>();
 		initVariant(mutation, constants, *v);
 
-		m_variants.pushBack(getAllocator(), hash, v);
+		m_variants.emplace(getAllocator(), hash, v);
 		variant = v;
 	}
 }

+ 1 - 1
src/anki/scene/SceneGraph.cpp

@@ -115,7 +115,7 @@ Error SceneGraph::registerNode(SceneNode* node)
 			return Error::USER_DATA;
 		}
 
-		m_nodesDict.pushBack(m_alloc, node->getName(), node);
+		m_nodesDict.emplace(m_alloc, node->getName(), node);
 	}
 
 	// Add to vector

+ 60 - 265
src/anki/util/HashMap.h

@@ -8,7 +8,7 @@
 #include <anki/util/Allocator.h>
 #include <anki/util/Functions.h>
 #include <anki/util/NonCopyable.h>
-#include <anki/util/Forward.h>
+#include <anki/util/SparseArray.h>
 
 namespace anki
 {
@@ -16,206 +16,86 @@ namespace anki
 /// @addtogroup util_containers
 /// @{
 
-namespace detail
-{
-
-/// HashMap node. It's not a traditional bucket because it doesn't contain more than one values.
-/// @internal
-template<typename TValue>
-class HashMapNode
+/// Default hasher.
+template<typename TKey>
+class DefaultHasher
 {
 public:
-	U64 m_hash = 0;
-	HashMapNode* m_left = nullptr;
-	HashMapNode* m_right = nullptr;
-	TValue m_value;
-	HashMapNode* m_parent = nullptr; ///< Used for iterating.
-
-	template<typename... TArgs>
-	HashMapNode(TArgs&&... args)
-		: m_value(std::forward<TArgs>(args)...)
-	{
-	}
-
-	TValue& getHashMapNodeValue()
-	{
-		return m_value;
-	}
-
-	const TValue& getHashMapNodeValue() const
+	U64 operator()(const TKey& a) const
 	{
-		return m_value;
+		return a.computeHash();
 	}
 };
 
-/// HashMap forward-only iterator.
-/// @internal
-template<typename TNodePointer, typename TValuePointer, typename TValueReference>
-class HashMapIterator
+/// Specialization for U64 keys.
+template<>
+class DefaultHasher<U64>
 {
-	template<typename, typename, typename>
-	friend class anki::HashMap;
-
 public:
-	/// Default constructor.
-	HashMapIterator()
-		: m_node(nullptr)
-	{
-	}
-
-	/// Copy.
-	HashMapIterator(const HashMapIterator& b)
-		: m_node(b.m_node)
-	{
-	}
-
-	/// Allow conversion from iterator to const iterator.
-	template<typename YNodePointer, typename YValuePointer, typename YValueReference>
-	HashMapIterator(const HashMapIterator<YNodePointer, YValuePointer, YValueReference>& b)
-		: m_node(b.m_node)
-	{
-	}
-
-	HashMapIterator(TNodePointer node)
-		: m_node(node)
-	{
-	}
-
-	TValueReference operator*() const
+	U64 operator()(const U64 a) const
 	{
-		ANKI_ASSERT(m_node);
-		return m_node->getHashMapNodeValue();
+		return a;
 	}
+};
 
-	TValuePointer operator->() const
-	{
-		ANKI_ASSERT(m_node);
-		return &m_node->getHashMapNodeValue();
-	}
+/// Hash map template.
+template<typename TKey, typename TValue, typename THasher = DefaultHasher<TKey>>
+class HashMap
+{
+public:
+	using SparseArrayType = SparseArray<TValue, U64>;
+	using Value = TValue;
+	using Key = TKey;
+	using Hasher = THasher;
+	using Iterator = typename SparseArrayType::Iterator;
+	using ConstIterator = typename SparseArrayType::ConstIterator;
 
-	HashMapIterator& operator++()
+	/// Default constructor.
+	HashMap()
 	{
-		ANKI_ASSERT(m_node);
-		TNodePointer node = m_node;
-
-		if(node->m_left)
-		{
-			node = node->m_left;
-		}
-		else if(node->m_right)
-		{
-			node = node->m_right;
-		}
-		else
-		{
-			// Node without children
-			TNodePointer prevNode = node;
-			node = node->m_parent;
-			while(node)
-			{
-				if(node->m_right && node->m_right != prevNode)
-				{
-					node = node->m_right;
-					break;
-				}
-				prevNode = node;
-				node = node->m_parent;
-			}
-		}
-
-		m_node = node;
-		return *this;
 	}
 
-	HashMapIterator operator++(int)
+	/// Move.
+	HashMap(HashMap&& b)
 	{
-		ANKI_ASSERT(m_node);
-		HashMapIterator out = *this;
-		++(*this);
-		return out;
+		*this = std::move(b);
 	}
 
-	HashMapIterator operator+(U n) const
+	/// You need to manually destroy the map.
+	/// @see HashMap::destroy
+	~HashMap()
 	{
-		HashMapIterator it = *this;
-		while(n-- != 0)
-		{
-			++it;
-		}
-		return it;
 	}
 
-	HashMapIterator& operator+=(U n)
+	/// Move.
+	HashMap& operator=(HashMap&& b)
 	{
-		while(n-- != 0)
-		{
-			++(*this);
-		}
+		m_sparseArr = std::move(b.m_sparseArr);
 		return *this;
 	}
 
-	Bool operator==(const HashMapIterator& b) const
-	{
-		return m_node == b.m_node;
-	}
-
-	Bool operator!=(const HashMapIterator& b) const
-	{
-		return !(*this == b);
-	}
-
-private:
-	TNodePointer m_node;
-};
-
-/// Hash map base.
-/// @tparam TKey The key of the map.
-/// @tparam TValue The value of the map.
-/// @tparam THasher Functor to hash type of TKey.
-/// @internal
-template<typename TKey, typename TValue, typename THasher, typename TNode>
-class HashMapBase : public NonCopyable
-{
-public:
-	using Key = TKey;
-	using Value = TValue;
-	using Reference = Value&;
-	using ConstReference = const Value&;
-	using Pointer = Value*;
-	using ConstPointer = const Value*;
-	using Iterator = HashMapIterator<TNode*, Pointer, Reference>;
-	using ConstIterator = HashMapIterator<const TNode*, ConstPointer, ConstReference>;
-
-	/// Default constructor.
-	HashMapBase()
-		: m_root(nullptr)
-	{
-	}
-
-	~HashMapBase() = default;
-
 	/// Get begin.
 	Iterator getBegin()
 	{
-		return Iterator(m_root);
+		return m_sparseArr.getBegin();
 	}
 
 	/// Get begin.
 	ConstIterator getBegin() const
 	{
-		return ConstIterator(m_root);
+		return m_sparseArr.getBegin();
 	}
 
 	/// Get end.
 	Iterator getEnd()
 	{
-		return Iterator();
+		return m_sparseArr.getEnd();
 	}
 
 	/// Get end.
 	ConstIterator getEnd() const
 	{
-		return ConstIterator();
+		return m_sparseArr.getEnd();
 	}
 
 	/// Get begin.
@@ -245,133 +125,48 @@ public:
 	/// Return true if map is empty.
 	Bool isEmpty() const
 	{
-		return m_root == nullptr;
-	}
-
-	/// Find item.
-	Iterator find(const Key& key);
-
-	/// Find item.
-	ConstIterator find(const Key& key) const;
-
-protected:
-	/// @privatesection
-	TNode* m_root = nullptr;
-
-	void move(HashMapBase& b)
-	{
-		m_root = b.m_root;
-		b.m_root = nullptr;
-	}
-
-	/// Add a node in the tree.
-	void insertNode(TNode* node);
-
-	/// Remove a node from the tree.
-	void removeNode(TNode* node);
-};
-
-} // end namespace detail
-
-/// Default hasher.
-template<typename TKey>
-class DefaultHasher
-{
-public:
-	U64 operator()(const TKey& a) const
-	{
-		return a.computeHash();
-	}
-};
-
-/// Specialization for U64 keys.
-template<>
-class DefaultHasher<U64>
-{
-public:
-	U64 operator()(const U64 a) const
-	{
-		return a;
+		return m_sparseArr.isEmpty();
 	}
-};
 
-/// Hash map template.
-template<typename TKey, typename TValue, typename THasher = DefaultHasher<TKey>>
-class HashMap : public detail::HashMapBase<TKey, TValue, THasher, detail::HashMapNode<TValue>>
-{
-private:
-	using Base = detail::HashMapBase<TKey, TValue, THasher, detail::HashMapNode<TValue>>;
-	using Node = detail::HashMapNode<TValue>;
-
-public:
-	using typename Base::Iterator;
-
-	/// Default constructor.
-	HashMap()
-		: Base()
-	{
-	}
-
-	/// Move.
-	HashMap(HashMap&& b)
-		: Base()
-	{
-		Base::move(b);
-	}
-
-	/// You need to manually destroy the map.
-	/// @see HashMap::destroy
-	~HashMap()
+	/// Destroy the list.
+	template<typename TAllocator>
+	void destroy(TAllocator alloc)
 	{
-		ANKI_ASSERT(Base::m_root == nullptr && "Requires manual destruction");
+		m_sparseArr.destroy(alloc);
 	}
 
-	/// Move.
-	HashMap& operator=(HashMap&& b)
+	/// Construct an element inside the map.
+	template<typename TAllocator, typename... TArgs>
+	Iterator emplace(TAllocator alloc, const TKey& key, TArgs&&... args)
 	{
-		Base::move(b);
-		return *this;
+		const U64 hash = THasher()(key);
+		return m_sparseArr.emplace(alloc, hash, std::forward<TArgs>(args)...);
 	}
 
-	/// Destroy the list.
-	template<typename TAllocator>
-	void destroy(TAllocator alloc);
-
-	/// Copy an element in the map.
+	/// Erase element.
 	template<typename TAllocator>
-	Iterator pushBack(TAllocator alloc, const TKey& key, const TValue& x)
+	void erase(TAllocator alloc, Iterator it)
 	{
-		Node* node = alloc.template newInstance<Node>(x);
-		node->m_hash = THasher()(key);
-		Base::insertNode(node);
-		return Iterator(node);
+		m_sparseArr.erase(alloc, it);
 	}
 
-	/// Construct an element inside the map.
-	template<typename TAllocator, typename... TArgs>
-	Iterator emplaceBack(TAllocator alloc, const TKey& key, TArgs&&... args)
+	/// Find a value using a key.
+	Iterator find(const Key& key)
 	{
-		Node* node = alloc.template newInstance<Node>(std::forward<TArgs>(args)...);
-		node->m_hash = THasher()(key);
-		Base::insertNode(node);
-		return Iterator(node);
+		const U64 hash = THasher()(key);
+		return m_sparseArr.find(hash);
 	}
 
-	/// Erase element.
-	template<typename TAllocator>
-	void erase(TAllocator alloc, typename Base::Iterator it)
+	/// Find a value using a key.
+	ConstIterator find(const Key& key) const
 	{
-		Node* del = it.m_node;
-		Base::removeNode(del);
-		alloc.deleteInstance(del);
+		const U64 hash = THasher()(key);
+		return m_sparseArr.find(hash);
 	}
 
 private:
-	template<typename TAllocator>
-	void destroyInternal(TAllocator alloc, Node* node);
+	SparseArrayType m_sparseArr;
 };
 /// @}
 
 } // end namespace anki
-
-#include <anki/util/HashMap.inl.h>

+ 0 - 215
src/anki/util/HashMap.inl.h

@@ -1,215 +0,0 @@
-// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-namespace anki
-{
-namespace detail
-{
-
-template<typename TKey, typename TValue, typename THasher, typename TNode>
-void HashMapBase<TKey, TValue, THasher, TNode>::insertNode(TNode* node)
-{
-	if(ANKI_UNLIKELY(!m_root))
-	{
-		m_root = node;
-		return;
-	}
-
-	const U64 hash = node->m_hash;
-	TNode* it = m_root;
-	Bool done = false;
-	do
-	{
-		const U64 nhash = it->m_hash;
-		if(hash > nhash)
-		{
-			// Go to right
-			TNode* right = it->m_right;
-			if(right)
-			{
-				it = right;
-			}
-			else
-			{
-				node->m_parent = it;
-				it->m_right = node;
-				done = true;
-			}
-		}
-		else
-		{
-			ANKI_ASSERT(hash != nhash && "Not supported");
-			// Go to left
-			TNode* left = it->m_left;
-			if(left)
-			{
-				it = left;
-			}
-			else
-			{
-				node->m_parent = it;
-				it->m_left = node;
-				done = true;
-			}
-		}
-	} while(!done);
-}
-
-template<typename TKey, typename TValue, typename THasher, typename TNode>
-typename HashMapBase<TKey, TValue, THasher, TNode>::Iterator HashMapBase<TKey, TValue, THasher, TNode>::find(
-	const Key& key)
-{
-	const U64 hash = THasher()(key);
-
-	TNode* node = m_root;
-	while(node)
-	{
-		const U64 bhash = node->m_hash;
-
-		if(hash < bhash)
-		{
-			node = node->m_left;
-		}
-		else if(hash > bhash)
-		{
-			node = node->m_right;
-		}
-		else
-		{
-			// Found
-			break;
-		}
-	}
-
-	return Iterator(node);
-}
-
-template<typename TKey, typename TValue, typename THasher, typename TNode>
-typename HashMapBase<TKey, TValue, THasher, TNode>::ConstIterator HashMapBase<TKey, TValue, THasher, TNode>::find(
-	const Key& key) const
-{
-	const U64 hash = THasher()(key);
-
-	const TNode* node = m_root;
-	while(node)
-	{
-		const U64 bhash = node->m_hash;
-
-		if(hash < bhash)
-		{
-			node = node->m_left;
-		}
-		else if(hash > bhash)
-		{
-			node = node->m_right;
-		}
-		else
-		{
-			// Found
-			break;
-		}
-	}
-
-	return ConstIterator(node);
-}
-
-template<typename TKey, typename TValue, typename THasher, typename TNode>
-void HashMapBase<TKey, TValue, THasher, TNode>::removeNode(TNode* del)
-{
-	ANKI_ASSERT(del);
-	TNode* parent = del->m_parent;
-	TNode* left = del->m_left;
-	TNode* right = del->m_right;
-
-	if(parent)
-	{
-		// If it has a parent then remove the connection to the parent and insert left and right like regular nodes
-
-		if(parent->m_left == del)
-		{
-			parent->m_left = nullptr;
-		}
-		else
-		{
-			ANKI_ASSERT(parent->m_right == del);
-			parent->m_right = nullptr;
-		}
-
-		if(left)
-		{
-			ANKI_ASSERT(left->m_parent == del);
-			left->m_parent = nullptr;
-			insertNode(left);
-		}
-
-		if(right)
-		{
-			ANKI_ASSERT(right->m_parent == del);
-			right->m_parent = nullptr;
-			insertNode(right);
-		}
-	}
-	else
-	{
-		// It's the root node. Make arbitrarily the left root and add the right
-
-		ANKI_ASSERT(del == m_root && "It must be the root");
-
-		if(left)
-		{
-			left->m_parent = nullptr;
-			m_root = left;
-
-			if(right)
-			{
-				right->m_parent = nullptr;
-				insertNode(right);
-			}
-		}
-		else
-		{
-			if(right)
-			{
-				right->m_parent = nullptr;
-			}
-
-			m_root = right;
-		}
-	}
-}
-
-} // end namespace detail
-
-template<typename TKey, typename TValue, typename THasher>
-template<typename TAllocator>
-void HashMap<TKey, TValue, THasher>::destroy(TAllocator alloc)
-{
-	if(Base::m_root)
-	{
-		destroyInternal(alloc, Base::m_root);
-		Base::m_root = nullptr;
-	}
-}
-
-template<typename TKey, typename TValue, typename THasher>
-template<typename TAllocator>
-void HashMap<TKey, TValue, THasher>::destroyInternal(TAllocator alloc, Node* node)
-{
-	ANKI_ASSERT(node);
-
-	if(node->m_right)
-	{
-		destroyInternal(alloc, node->m_right);
-	}
-
-	if(node->m_left)
-	{
-		destroyInternal(alloc, node->m_left);
-	}
-
-	alloc.deleteInstance(node);
-}
-
-} // end namespace anki

+ 5 - 2
src/anki/util/SparseArray.h

@@ -306,7 +306,7 @@ public:
 	/// Return true if it's empty and false otherwise.
 	Bool isEmpty() const
 	{
-		return m_elementCount != 0;
+		return m_elementCount == 0;
 	}
 
 	/// Destroy the array and free its elements.
@@ -315,7 +315,7 @@ public:
 
 	/// Set a value to an index.
 	template<typename TAlloc, typename... TArgs>
-	void emplace(TAlloc& alloc, Index idx, TArgs&&... args);
+	Iterator emplace(TAlloc& alloc, Index idx, TArgs&&... args);
 
 	/// Get an iterator.
 	Iterator find(Index idx)
@@ -464,6 +464,9 @@ protected:
 		return (pos >= m_capacity) ? MAX_U32 : pos;
 	}
 
+	template<typename TAlloc, typename... TArgs>
+	void emplaceInternal(TAlloc& alloc, Index idx, TArgs&&... args);
+
 	void destroyElement(Value& v)
 	{
 		v.~Value();

+ 16 - 1
src/anki/util/SparseArray.inl.h

@@ -33,7 +33,7 @@ void SparseArray<T, TIndex>::destroy(TAlloc& alloc)
 
 template<typename T, typename TIndex>
 template<typename TAlloc, typename... TArgs>
-void SparseArray<T, TIndex>::emplace(TAlloc& alloc, Index idx, TArgs&&... args)
+void SparseArray<T, TIndex>::emplaceInternal(TAlloc& alloc, Index idx, TArgs&&... args)
 {
 	if(m_capacity == 0 || calcLoadFactor() > m_maxLoadFactor)
 	{
@@ -46,6 +46,21 @@ void SparseArray<T, TIndex>::emplace(TAlloc& alloc, Index idx, TArgs&&... args)
 	invalidateIterators();
 }
 
+template<typename T, typename TIndex>
+template<typename TAlloc, typename... TArgs>
+typename SparseArray<T, TIndex>::Iterator SparseArray<T, TIndex>::emplace(TAlloc& alloc, Index idx, TArgs&&... args)
+{
+	emplaceInternal(alloc, idx, std::forward<TArgs>(args)...);
+
+	return Iterator(this,
+		findInternal(idx)
+#if ANKI_EXTRA_CHECKS
+			,
+		m_iteratorVer
+#endif
+		);
+}
+
 template<typename T, typename TIndex>
 template<typename TAlloc>
 U32 SparseArray<T, TIndex>::insert(TAlloc& alloc, Index idx, Value& val)

+ 8 - 8
tests/util/HashMap.cpp

@@ -31,8 +31,8 @@ ANKI_TEST(Util, HashMap)
 	// Simple
 	{
 		HashMap<int, int, Hasher> map;
-		map.pushBack(alloc, 20, 1);
-		map.pushBack(alloc, 21, 1);
+		map.emplace(alloc, 20, 1);
+		map.emplace(alloc, 21, 1);
 		map.destroy(alloc);
 	}
 
@@ -42,7 +42,7 @@ ANKI_TEST(Util, HashMap)
 
 		for(U i = 0; i < valsSize; ++i)
 		{
-			map.pushBack(alloc, vals[i], vals[i] * 10);
+			map.emplace(alloc, vals[i], vals[i] * 10);
 		}
 
 		U count = 0;
@@ -62,7 +62,7 @@ ANKI_TEST(Util, HashMap)
 
 		for(U i = 0; i < valsSize; ++i)
 		{
-			map.pushBack(alloc, vals[i], vals[i] * 10);
+			map.emplace(alloc, vals[i], vals[i] * 10);
 		}
 
 		for(U i = valsSize - 1; i != 0; --i)
@@ -71,7 +71,7 @@ ANKI_TEST(Util, HashMap)
 			ANKI_TEST_EXPECT_NEQ(it, map.getEnd());
 
 			map.erase(alloc, it);
-			map.pushBack(alloc, vals[i], vals[i] * 10);
+			map.emplace(alloc, vals[i], vals[i] * 10);
 		}
 
 		map.destroy(alloc);
@@ -83,7 +83,7 @@ ANKI_TEST(Util, HashMap)
 
 		for(U i = 0; i < valsSize; ++i)
 		{
-			map.pushBack(alloc, vals[i], vals[i] * 10);
+			map.emplace(alloc, vals[i], vals[i] * 10);
 		}
 
 		for(U i = valsSize - 1; i != 0; --i)
@@ -113,7 +113,7 @@ ANKI_TEST(Util, HashMap)
 				{
 					// Not found
 					ANKI_TEST_EXPECT_EQ(akMap.find(num), akMap.getEnd());
-					akMap.pushBack(alloc, num, num);
+					akMap.emplace(alloc, num, num);
 					numbers.push_back(num);
 					break;
 				}
@@ -175,7 +175,7 @@ ANKI_TEST(Util, HashMap)
 		timer.start();
 		for(U i = 0; i < COUNT; ++i)
 		{
-			akMap.pushBack(alloc, vals[i], vals[i]);
+			akMap.emplace(alloc, vals[i], vals[i]);
 		}
 		timer.stop();
 		Second akTime = timer.getElapsedTime();