BAN: Cleanup HashSet
I now use BAN::Iterator as the base iterator. Also unstable version was added.
This commit is contained in:
		
							parent
							
								
									bacc0db778
								
							
						
					
					
						commit
						79a15132da
					
				| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <BAN/Errors.h>
 | 
					#include <BAN/Errors.h>
 | 
				
			||||||
#include <BAN/Hash.h>
 | 
					#include <BAN/Hash.h>
 | 
				
			||||||
 | 
					#include <BAN/Iterators.h>
 | 
				
			||||||
#include <BAN/Math.h>
 | 
					#include <BAN/Math.h>
 | 
				
			||||||
#include <BAN/Move.h>
 | 
					#include <BAN/Move.h>
 | 
				
			||||||
#include <BAN/Vector.h>
 | 
					#include <BAN/Vector.h>
 | 
				
			||||||
| 
						 | 
					@ -9,24 +10,22 @@
 | 
				
			||||||
namespace BAN
 | 
					namespace BAN
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH = hash<T>, bool STABLE = true>
 | 
				
			||||||
	class HashSetIterator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH = hash<T>>
 | 
					 | 
				
			||||||
	class HashSet
 | 
						class HashSet
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
		using value_type = T;
 | 
							using value_type = T;
 | 
				
			||||||
		using size_type = hash_t;
 | 
							using size_type = size_t;
 | 
				
			||||||
		using const_iterator = HashSetIterator<T, HASH>;
 | 
							using iterator = IteratorDouble<T, Vector, Vector, HashSet>;
 | 
				
			||||||
 | 
							using const_iterator = ConstIteratorDouble<T, Vector, Vector, HashSet>;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
		HashSet() = default;
 | 
							HashSet() = default;
 | 
				
			||||||
		HashSet(const HashSet<T, HASH>&);
 | 
							HashSet(const HashSet&);
 | 
				
			||||||
		HashSet(HashSet<T, HASH>&&);
 | 
							HashSet(HashSet&&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
 | 
							HashSet& operator=(const HashSet&);
 | 
				
			||||||
		HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
 | 
							HashSet& operator=(HashSet&&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ErrorOr<void> insert(const T&);
 | 
							ErrorOr<void> insert(const T&);
 | 
				
			||||||
		ErrorOr<void> insert(T&&);
 | 
							ErrorOr<void> insert(T&&);
 | 
				
			||||||
| 
						 | 
					@ -35,8 +34,10 @@ namespace BAN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ErrorOr<void> reserve(size_type);
 | 
							ErrorOr<void> reserve(size_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
 | 
							iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
 | 
				
			||||||
		const_iterator end() const   { return const_iterator(this, m_buckets.end()); }
 | 
							iterator end()   { return iterator(m_buckets.end(), m_buckets.end()); }
 | 
				
			||||||
 | 
							const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
 | 
				
			||||||
 | 
							const_iterator end() const   { return const_iterator(m_buckets.end(), m_buckets.end()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool contains(const T&) const;
 | 
							bool contains(const T&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,57 +52,25 @@ namespace BAN
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
		Vector<Vector<T>> m_buckets;
 | 
							Vector<Vector<T>> m_buckets;
 | 
				
			||||||
		size_type m_size = 0;
 | 
							size_type m_size = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		friend class HashSetIterator<T, HASH>;
 | 
					 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	class HashSetIterator
 | 
						HashSet<T, HASH, STABLE>::HashSet(const HashSet& other)
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
	public:
 | 
					 | 
				
			||||||
		HashSetIterator() = default;
 | 
					 | 
				
			||||||
		HashSetIterator(const HashSetIterator<T, HASH>&);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		HashSetIterator<T, HASH>& operator++();
 | 
					 | 
				
			||||||
		HashSetIterator<T, HASH> operator++(int);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const T& operator*() const;
 | 
					 | 
				
			||||||
		const T* operator->() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bool operator==(const HashSetIterator<T, HASH>&) const;
 | 
					 | 
				
			||||||
		bool operator!=(const HashSetIterator<T, HASH>&) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		operator bool() const { return m_owner && m_current_bucket; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private:
 | 
					 | 
				
			||||||
		HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket);
 | 
					 | 
				
			||||||
		void find_next();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private:
 | 
					 | 
				
			||||||
		const HashSet<T, HASH>*				m_owner = nullptr;
 | 
					 | 
				
			||||||
		Vector<Vector<T>>::const_iterator	m_current_bucket;
 | 
					 | 
				
			||||||
		Vector<T>::const_iterator			m_current_key;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		friend class HashSet<T, HASH>;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
 | 
					 | 
				
			||||||
		: m_buckets(other.m_buckets)
 | 
							: m_buckets(other.m_buckets)
 | 
				
			||||||
		, m_size(other.m_size)
 | 
							, m_size(other.m_size)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
 | 
						HashSet<T, HASH, STABLE>::HashSet(HashSet&& other)
 | 
				
			||||||
		: m_buckets(move(other.m_buckets))
 | 
							: m_buckets(move(other.m_buckets))
 | 
				
			||||||
		, m_size(other.m_size)
 | 
							, m_size(other.m_size)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		other.clear();
 | 
							other.clear();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
 | 
						HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(const HashSet& other)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		clear();
 | 
							clear();
 | 
				
			||||||
		m_buckets = other.m_buckets;
 | 
							m_buckets = other.m_buckets;
 | 
				
			||||||
| 
						 | 
					@ -109,8 +78,8 @@ namespace BAN
 | 
				
			||||||
		return *this;
 | 
							return *this;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
 | 
						HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(HashSet&& other)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		clear();
 | 
							clear();
 | 
				
			||||||
		m_buckets = move(other.m_buckets);
 | 
							m_buckets = move(other.m_buckets);
 | 
				
			||||||
| 
						 | 
					@ -119,14 +88,14 @@ namespace BAN
 | 
				
			||||||
		return *this;
 | 
							return *this;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
 | 
						ErrorOr<void> HashSet<T, HASH, STABLE>::insert(const T& key)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return insert(move(T(key)));
 | 
							return insert(move(T(key)));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
 | 
						ErrorOr<void> HashSet<T, HASH, STABLE>::insert(T&& key)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!empty() && get_bucket(key).contains(key))
 | 
							if (!empty() && get_bucket(key).contains(key))
 | 
				
			||||||
			return {};
 | 
								return {};
 | 
				
			||||||
| 
						 | 
					@ -137,8 +106,8 @@ namespace BAN
 | 
				
			||||||
		return {};
 | 
							return {};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	void HashSet<T, HASH>::remove(const T& key)
 | 
						void HashSet<T, HASH, STABLE>::remove(const T& key)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (empty()) return;
 | 
							if (empty()) return;
 | 
				
			||||||
		Vector<T>& bucket = get_bucket(key);
 | 
							Vector<T>& bucket = get_bucket(key);
 | 
				
			||||||
| 
						 | 
					@ -153,41 +122,41 @@ namespace BAN
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	void HashSet<T, HASH>::clear()
 | 
						void HashSet<T, HASH, STABLE>::clear()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		m_buckets.clear();
 | 
							m_buckets.clear();
 | 
				
			||||||
		m_size = 0;
 | 
							m_size = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
 | 
						ErrorOr<void> HashSet<T, HASH, STABLE>::reserve(size_type size)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		TRY(rebucket(size));
 | 
							TRY(rebucket(size));
 | 
				
			||||||
		return {};
 | 
							return {};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	bool HashSet<T, HASH>::contains(const T& key) const
 | 
						bool HashSet<T, HASH, STABLE>::contains(const T& key) const
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (empty()) return false;
 | 
							if (empty()) return false;
 | 
				
			||||||
		return get_bucket(key).contains(key);
 | 
							return get_bucket(key).contains(key);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
 | 
						typename HashSet<T, HASH, STABLE>::size_type HashSet<T, HASH, STABLE>::size() const
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_size;
 | 
							return m_size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	bool HashSet<T, HASH>::empty() const
 | 
						bool HashSet<T, HASH, STABLE>::empty() const
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_size == 0;
 | 
							return m_size == 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
 | 
						ErrorOr<void> HashSet<T, HASH, STABLE>::rebucket(size_type bucket_count)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (m_buckets.size() >= bucket_count)
 | 
							if (m_buckets.size() >= bucket_count)
 | 
				
			||||||
			return {};
 | 
								return {};
 | 
				
			||||||
| 
						 | 
					@ -197,15 +166,15 @@ namespace BAN
 | 
				
			||||||
		if (new_buckets.resize(new_bucket_count).is_error())
 | 
							if (new_buckets.resize(new_bucket_count).is_error())
 | 
				
			||||||
			return Error::from_errno(ENOMEM);
 | 
								return Error::from_errno(ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// NOTE: We have to copy the old keys to the new keys and not move
 | 
							for (auto& bucket : m_buckets)
 | 
				
			||||||
		//       since we might run out of memory half way through.
 | 
					 | 
				
			||||||
		for (Vector<T>& bucket : m_buckets)
 | 
					 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			for (T& key : bucket)
 | 
								for (T& key : bucket)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				size_type bucket_index = HASH()(key) % new_buckets.size();
 | 
									size_type bucket_index = HASH()(key) % new_buckets.size();
 | 
				
			||||||
				if (new_buckets[bucket_index].push_back(key).is_error())
 | 
									if constexpr(STABLE)
 | 
				
			||||||
					return Error::from_errno(ENOMEM);
 | 
										TRY(new_buckets[bucket_index].push_back(key));
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										TRY(new_buckets[bucket_index].push_back(move(key)));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,92 +182,27 @@ namespace BAN
 | 
				
			||||||
		return {};
 | 
							return {};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
 | 
						Vector<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ASSERT(!m_buckets.empty());
 | 
							ASSERT(!m_buckets.empty());
 | 
				
			||||||
		size_type index = HASH()(key) % m_buckets.size();
 | 
							size_type index = HASH()(key) % m_buckets.size();
 | 
				
			||||||
		return m_buckets[index];
 | 
							return m_buckets[index];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						template<typename T, typename HASH, bool STABLE>
 | 
				
			||||||
	const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
 | 
						const Vector<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key) const
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ASSERT(!m_buckets.empty());
 | 
							ASSERT(!m_buckets.empty());
 | 
				
			||||||
		size_type index = HASH()(key) % m_buckets.size();
 | 
							size_type index = HASH()(key) % m_buckets.size();
 | 
				
			||||||
		return m_buckets[index];
 | 
							return m_buckets[index];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename T, typename HASH>
 | 
						// Unstable hash set moves values between container during rebucketing.
 | 
				
			||||||
	HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
 | 
						// This means that if insertion to set fails, elements could be in invalid state
 | 
				
			||||||
	{
 | 
						// and that container is no longer usable. This is better if either way you are
 | 
				
			||||||
		ASSERT(*this);
 | 
						// going to stop using the hash set after insertion fails.
 | 
				
			||||||
		if (m_current_key == m_current_bucket->end())
 | 
						template<typename T, typename HASH = BAN::hash<T>>
 | 
				
			||||||
			m_current_bucket++;
 | 
						using HashSetUnstable = HashSet<T, HASH, false>;
 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			m_current_key++;
 | 
					 | 
				
			||||||
		find_next();
 | 
					 | 
				
			||||||
		return *this;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	HashSetIterator<T, HASH> HashSetIterator<T, HASH>::operator++(int)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto temp = *this;
 | 
					 | 
				
			||||||
		++(*this);
 | 
					 | 
				
			||||||
		return temp;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	const T& HashSetIterator<T, HASH>::operator*() const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ASSERT(m_owner && m_current_bucket && m_current_key);
 | 
					 | 
				
			||||||
		return *m_current_key;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	const T* HashSetIterator<T, HASH>::operator->() const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return &**this;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	bool HashSetIterator<T, HASH>::operator==(const HashSetIterator<T, HASH>& other) const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (!m_owner || m_owner != other.m_owner)
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		return m_current_bucket == other.m_current_bucket
 | 
					 | 
				
			||||||
			&& m_current_key    == other.m_current_key;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	bool HashSetIterator<T, HASH>::operator!=(const HashSetIterator<T, HASH>& other) const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return !(*this == other);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	HashSetIterator<T, HASH>::HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket)
 | 
					 | 
				
			||||||
		: m_owner(owner)
 | 
					 | 
				
			||||||
		, m_current_bucket(bucket)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (m_current_bucket != m_owner->m_buckets.end())
 | 
					 | 
				
			||||||
			m_current_key = m_current_bucket->begin();
 | 
					 | 
				
			||||||
		find_next();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T, typename HASH>
 | 
					 | 
				
			||||||
	void HashSetIterator<T, HASH>::find_next()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ASSERT(m_owner && m_current_bucket);
 | 
					 | 
				
			||||||
		while (m_current_bucket != m_owner->m_buckets.end())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (m_current_key && m_current_key != m_current_bucket->end())
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			m_current_bucket++;
 | 
					 | 
				
			||||||
			m_current_key = m_current_bucket->begin();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		m_current_key = typename Vector<T>::const_iterator();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue