From 79a15132dadeae6b9c04528159e0c9ec02f8166e Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 23 Dec 2023 16:32:06 +0200 Subject: [PATCH] BAN: Cleanup HashSet I now use BAN::Iterator as the base iterator. Also unstable version was added. --- BAN/include/BAN/HashSet.h | 206 ++++++++++---------------------------- 1 file changed, 55 insertions(+), 151 deletions(-) diff --git a/BAN/include/BAN/HashSet.h b/BAN/include/BAN/HashSet.h index 58845fc893..37bd10747a 100644 --- a/BAN/include/BAN/HashSet.h +++ b/BAN/include/BAN/HashSet.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -9,24 +10,22 @@ namespace BAN { - template - class HashSetIterator; - - template> + template, bool STABLE = true> class HashSet { public: using value_type = T; - using size_type = hash_t; - using const_iterator = HashSetIterator; + using size_type = size_t; + using iterator = IteratorDouble; + using const_iterator = ConstIteratorDouble; public: HashSet() = default; - HashSet(const HashSet&); - HashSet(HashSet&&); + HashSet(const HashSet&); + HashSet(HashSet&&); - HashSet& operator=(const HashSet&); - HashSet& operator=(HashSet&&); + HashSet& operator=(const HashSet&); + HashSet& operator=(HashSet&&); ErrorOr insert(const T&); ErrorOr insert(T&&); @@ -35,8 +34,10 @@ namespace BAN ErrorOr reserve(size_type); - const_iterator begin() const { return const_iterator(this, m_buckets.begin()); } - const_iterator end() const { return const_iterator(this, m_buckets.end()); } + iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); } + 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; @@ -51,57 +52,25 @@ namespace BAN private: Vector> m_buckets; size_type m_size = 0; - - friend class HashSetIterator; }; - template - class HashSetIterator - { - public: - HashSetIterator() = default; - HashSetIterator(const HashSetIterator&); - - HashSetIterator& operator++(); - HashSetIterator operator++(int); - - const T& operator*() const; - const T* operator->() const; - - bool operator==(const HashSetIterator&) const; - bool operator!=(const HashSetIterator&) const; - - operator bool() const { return m_owner && m_current_bucket; } - - private: - HashSetIterator(const HashSet* owner, Vector>::const_iterator bucket); - void find_next(); - - private: - const HashSet* m_owner = nullptr; - Vector>::const_iterator m_current_bucket; - Vector::const_iterator m_current_key; - - friend class HashSet; - }; - - template - HashSet::HashSet(const HashSet& other) + template + HashSet::HashSet(const HashSet& other) : m_buckets(other.m_buckets) , m_size(other.m_size) { } - template - HashSet::HashSet(HashSet&& other) + template + HashSet::HashSet(HashSet&& other) : m_buckets(move(other.m_buckets)) , m_size(other.m_size) { other.clear(); } - template - HashSet& HashSet::operator=(const HashSet& other) + template + HashSet& HashSet::operator=(const HashSet& other) { clear(); m_buckets = other.m_buckets; @@ -109,8 +78,8 @@ namespace BAN return *this; } - template - HashSet& HashSet::operator=(HashSet&& other) + template + HashSet& HashSet::operator=(HashSet&& other) { clear(); m_buckets = move(other.m_buckets); @@ -119,14 +88,14 @@ namespace BAN return *this; } - template - ErrorOr HashSet::insert(const T& key) + template + ErrorOr HashSet::insert(const T& key) { return insert(move(T(key))); } - template - ErrorOr HashSet::insert(T&& key) + template + ErrorOr HashSet::insert(T&& key) { if (!empty() && get_bucket(key).contains(key)) return {}; @@ -137,8 +106,8 @@ namespace BAN return {}; } - template - void HashSet::remove(const T& key) + template + void HashSet::remove(const T& key) { if (empty()) return; Vector& bucket = get_bucket(key); @@ -153,41 +122,41 @@ namespace BAN } } - template - void HashSet::clear() + template + void HashSet::clear() { m_buckets.clear(); m_size = 0; } - template - ErrorOr HashSet::reserve(size_type size) + template + ErrorOr HashSet::reserve(size_type size) { TRY(rebucket(size)); return {}; } - template - bool HashSet::contains(const T& key) const + template + bool HashSet::contains(const T& key) const { if (empty()) return false; return get_bucket(key).contains(key); } - template - typename HashSet::size_type HashSet::size() const + template + typename HashSet::size_type HashSet::size() const { return m_size; } - template - bool HashSet::empty() const + template + bool HashSet::empty() const { return m_size == 0; } - template - ErrorOr HashSet::rebucket(size_type bucket_count) + template + ErrorOr HashSet::rebucket(size_type bucket_count) { if (m_buckets.size() >= bucket_count) return {}; @@ -196,16 +165,16 @@ namespace BAN Vector> new_buckets; if (new_buckets.resize(new_bucket_count).is_error()) return Error::from_errno(ENOMEM); - - // NOTE: We have to copy the old keys to the new keys and not move - // since we might run out of memory half way through. - for (Vector& bucket : m_buckets) + + for (auto& bucket : m_buckets) { for (T& key : bucket) { size_type bucket_index = HASH()(key) % new_buckets.size(); - if (new_buckets[bucket_index].push_back(key).is_error()) - return Error::from_errno(ENOMEM); + if constexpr(STABLE) + 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 {}; } - template - Vector& HashSet::get_bucket(const T& key) + template + Vector& HashSet::get_bucket(const T& key) { ASSERT(!m_buckets.empty()); size_type index = HASH()(key) % m_buckets.size(); return m_buckets[index]; } - template - const Vector& HashSet::get_bucket(const T& key) const + template + const Vector& HashSet::get_bucket(const T& key) const { ASSERT(!m_buckets.empty()); size_type index = HASH()(key) % m_buckets.size(); return m_buckets[index]; } - template - HashSetIterator& HashSetIterator::operator++() - { - ASSERT(*this); - if (m_current_key == m_current_bucket->end()) - m_current_bucket++; - else - m_current_key++; - find_next(); - return *this; - } - - template - HashSetIterator HashSetIterator::operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - template - const T& HashSetIterator::operator*() const - { - ASSERT(m_owner && m_current_bucket && m_current_key); - return *m_current_key; - } - - template - const T* HashSetIterator::operator->() const - { - return &**this; - } - - template - bool HashSetIterator::operator==(const HashSetIterator& 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 - bool HashSetIterator::operator!=(const HashSetIterator& other) const - { - return !(*this == other); - } - - template - HashSetIterator::HashSetIterator(const HashSet* owner, Vector>::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 - void HashSetIterator::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::const_iterator(); - } + // Unstable hash set moves values between container during rebucketing. + // 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 + // going to stop using the hash set after insertion fails. + template> + using HashSetUnstable = HashSet; } \ No newline at end of file