Vulkan/Debug: Add WeakMap

WeakMap is an associative container of keys of type K to values of type std::weak_ptr<V>.
WeakMap's iterators will skip over elements where the value has no more remaining std::shared_ptr<V> references.

Used to automatically track live debug objects.

Bug: b/145351270
Change-Id: I87b9a9be4b83ff887510f292a4e03770ccc9eeaf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38894
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/Debug/WeakMap.hpp b/src/Vulkan/Debug/WeakMap.hpp
new file mode 100644
index 0000000..984ca05
--- /dev/null
+++ b/src/Vulkan/Debug/WeakMap.hpp
@@ -0,0 +1,199 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef VK_DEBUG_WEAKMAP_HPP_
+#define VK_DEBUG_WEAKMAP_HPP_
+
+#include <map>
+#include <memory>
+
+namespace vk
+{
+namespace dbg
+{
+
+// WeakMap is an associative container of keys of type K to values of type
+// std::weak_ptr<V>.
+// WeakMap's iterators will skip over elements where the value has no more
+// remaining std::shared_ptr<V> references.
+// WeakMap is not thread-safe and requires the use of an external mutex to be
+// used by multiple threads, concurrently.
+template <typename K, typename V>
+class WeakMap
+{
+	using Map = std::map<K, std::weak_ptr<V>>;
+	using MapIterator = typename Map::const_iterator;
+
+public:
+	class iterator
+	{
+	public:
+		inline iterator(const MapIterator& it, const MapIterator& end);
+		inline void operator++();
+		inline bool operator==(const iterator&) const;
+		inline bool operator!=(const iterator&) const;
+		inline std::pair<K, std::shared_ptr<V>> operator*() const;
+
+	private:
+		void skipNull();
+
+		MapIterator it;
+		const MapIterator end;
+		std::shared_ptr<V> sptr;
+	};
+
+	// begin() returns an iterator to the start of the map.
+	inline iterator begin() const;
+
+	// end() returns an iterator to the end of the map.
+	inline iterator end() const;
+
+	// approx_size() returns an approximate number of entries in the map. This
+	// is guaranteed to be greater than or equal to the actual number of
+	// elements in the map.
+	inline size_t approx_size() const;
+
+	// get() returns the std::shared_ptr<V> value for the given key, or nullptr
+	// if the map does not contain the key, or the last remaining
+	// std::shared_ptr<V> reference to the value has been dropped.
+	inline std::shared_ptr<V> get(const K& key) const;
+
+	// add() attempts to insert the key-value pair into the map.
+	// add() returns true if there was no existing entry with the given key,
+	// and the pair was added, otherwise false.
+	inline bool add(const K& key, const std::shared_ptr<V>& val);
+
+	// remove() attempts to remove the entry with the given key from the map.
+	// remove() returns true if there was no existing entry with the given key,
+	// and the entry was removed, otherwise false.
+	inline bool remove(const K& key);
+
+private:
+	// reap() removes any entries that have values with no external references.
+	inline void reap();
+
+	Map map;
+	size_t reapAtSize = 32;
+};
+
+template <typename K, typename V>
+WeakMap<K, V>::iterator::iterator(const MapIterator& it, const MapIterator& end) :
+    it(it),
+    end(end)
+{
+	skipNull();
+}
+
+template <typename K, typename V>
+void WeakMap<K, V>::iterator::operator++()
+{
+	it++;
+	skipNull();
+}
+
+template <typename K, typename V>
+void WeakMap<K, V>::iterator::skipNull()
+{
+	for(; it != end; ++it)
+	{
+		// Hold on to the shared_ptr when pointing at this map element.
+		// This ensures that the object is not released.
+		sptr = it->second.lock();
+		if(sptr)
+		{
+			return;
+		}
+	}
+}
+
+template <typename K, typename V>
+bool WeakMap<K, V>::iterator::operator==(const iterator& rhs) const
+{
+	return it == rhs.it;
+}
+
+template <typename K, typename V>
+bool WeakMap<K, V>::iterator::operator!=(const iterator& rhs) const
+{
+	return it != rhs.it;
+}
+
+template <typename K, typename V>
+std::pair<K, std::shared_ptr<V>> WeakMap<K, V>::iterator::operator*() const
+{
+	return { it->first, sptr };
+}
+
+template <typename K, typename V>
+typename WeakMap<K, V>::iterator WeakMap<K, V>::begin() const
+{
+	return iterator(map.begin(), map.end());
+}
+
+template <typename K, typename V>
+typename WeakMap<K, V>::iterator WeakMap<K, V>::end() const
+{
+	return iterator(map.end(), map.end());
+}
+
+template <typename K, typename V>
+size_t WeakMap<K, V>::approx_size() const
+{
+	return map.size();
+}
+
+template <typename K, typename V>
+std::shared_ptr<V> WeakMap<K, V>::get(const K& key) const
+{
+	auto it = map.find(key);
+	return (it != map.end()) ? it->second.lock() : nullptr;
+}
+
+template <typename K, typename V>
+bool WeakMap<K, V>::add(const K& key, const std::shared_ptr<V>& val)
+{
+	if(map.size() > reapAtSize)
+	{
+		reap();
+		reapAtSize = map.size() * 2 + 32;
+	}
+	return map.emplace(key, val).second;
+}
+
+template <typename K, typename V>
+bool WeakMap<K, V>::remove(const K& key)
+{
+	return map.erase(key) > 0;
+}
+
+template <typename K, typename V>
+void WeakMap<K, V>::reap()
+{
+	for(auto it = map.begin(); it != map.end();)
+	{
+		if(it->second.expired())
+		{
+			map.erase(it++);
+		}
+		else
+		{
+			++it;
+		}
+	}
+}
+
+}  // namespace dbg
+}  // namespace vk
+
+#endif  // VK_DEBUG_WEAKMAP_HPP_