#pragma once #include #include #include #include #include namespace BAN { template class HashSetIterator; template> class HashSet { public: using value_type = T; using size_type = hash_t; using const_iterator = HashSetIterator; public: HashSet() = default; HashSet(const HashSet&); HashSet(HashSet&&); HashSet& operator=(const HashSet&); HashSet& operator=(HashSet&&); ErrorOr insert(const T&); ErrorOr insert(T&&); void remove(const T&); void clear(); 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()); } bool contains(const T&) const; size_type size() const; bool empty() const; private: ErrorOr rebucket(size_type); Vector& get_bucket(const T&); const Vector& get_bucket(const T&) const; 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) : m_buckets(other.m_buckets) , m_size(other.m_size) { } 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) { clear(); m_buckets = other.m_buckets; m_size = other.m_size; return *this; } template HashSet& HashSet::operator=(HashSet&& other) { clear(); m_buckets = move(other.m_buckets); m_size = other.m_size; other.clear(); return *this; } template ErrorOr HashSet::insert(const T& key) { return insert(move(T(key))); } template ErrorOr HashSet::insert(T&& key) { if (!empty() && get_bucket(key).contains(key)) return {}; TRY(rebucket(m_size + 1)); TRY(get_bucket(key).push_back(move(key))); m_size++; return {}; } template void HashSet::remove(const T& key) { if (empty()) return; Vector& bucket = get_bucket(key); for (size_type i = 0; i < bucket.size(); i++) { if (bucket[i] == key) { bucket.remove(i); m_size--; break; } } } template void HashSet::clear() { m_buckets.clear(); m_size = 0; } template ErrorOr HashSet::reserve(size_type size) { TRY(rebucket(size)); return {}; } 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 { return m_size; } template bool HashSet::empty() const { return m_size == 0; } template ErrorOr HashSet::rebucket(size_type bucket_count) { if (m_buckets.size() >= bucket_count) return {}; size_type new_bucket_count = BAN::Math::max(bucket_count, m_buckets.size() * 2); 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 (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); } } m_buckets = move(new_buckets); return {}; } 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 { 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(); } }