#pragma once #include #include #include namespace BAN { template> class HashMap { public: struct Entry { template Entry(const Key& key, Args&&... args) requires is_constructible_v : key(key) , value(forward(args)...) {} template Entry(Key&& key, Args&&... args) requires is_constructible_v : key(BAN::move(key)) , value(forward(args)...) {} Key key; T value; }; public: using size_type = size_t; using key_type = Key; using value_type = T; using iterator = IteratorDouble; using const_iterator = ConstIteratorDouble; public: HashMap() = default; HashMap(const HashMap&); HashMap(HashMap&&); ~HashMap(); HashMap& operator=(const HashMap&); HashMap& operator=(HashMap&&); ErrorOr insert(const Key& key, const T& value) { return emplace(key, value); } ErrorOr insert(const Key& key, T&& value) { return emplace(key, move(value)); } ErrorOr insert(Key&& key, const T& value) { return emplace(move(key), value); } ErrorOr insert(Key&& key, T&& value) { return emplace(move(key), move(value)); } ErrorOr insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); } ErrorOr insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); } ErrorOr insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); } ErrorOr insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); } template ErrorOr emplace(const Key& key, Args&&... args) requires is_constructible_v { return emplace(Key(key), forward(args)...); } template ErrorOr emplace(Key&&, Args&&...) requires is_constructible_v; template ErrorOr emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v { return emplace_or_assign(Key(key), forward(args)...); } template ErrorOr emplace_or_assign(Key&&, Args&&...) requires is_constructible_v; 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()); } ErrorOr reserve(size_type); void remove(const Key&); void remove(iterator it); void clear(); T& operator[](const Key&); const T& operator[](const Key&) const; iterator find(const Key& key); const_iterator find(const Key& key) const; bool contains(const Key&) const; bool empty() const; size_type size() const; private: ErrorOr rebucket(size_type); LinkedList& get_bucket(const Key&); const LinkedList& get_bucket(const Key&) const; Vector>::iterator get_bucket_iterator(const Key&); Vector>::const_iterator get_bucket_iterator(const Key&) const; private: Vector> m_buckets; size_type m_size = 0; friend iterator; }; template HashMap::HashMap(const HashMap& other) { *this = other; } template HashMap::HashMap(HashMap&& other) { *this = move(other); } template HashMap::~HashMap() { clear(); } template HashMap& HashMap::operator=(const HashMap& other) { clear(); m_buckets = other.m_buckets; m_size = other.m_size; return *this; } template HashMap& HashMap::operator=(HashMap&& other) { clear(); m_buckets = move(other.m_buckets); m_size = other.m_size; other.m_size = 0; return *this; } template template ErrorOr::iterator> HashMap::emplace(Key&& key, Args&&... args) requires is_constructible_v { ASSERT(!contains(key)); TRY(rebucket(m_size + 1)); auto bucket_it = get_bucket_iterator(key); TRY(bucket_it->emplace_back(move(key), forward(args)...)); m_size++; return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1)); } template template ErrorOr::iterator> HashMap::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v { if (empty()) return emplace(move(key), forward(args)...); auto bucket_it = get_bucket_iterator(key); for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++) { if (entry_it->key != key) continue; entry_it->value = T(forward(args)...); return iterator(m_buckets.end(), bucket_it, entry_it); } return emplace(move(key), forward(args)...); } template ErrorOr HashMap::reserve(size_type size) { TRY(rebucket(size)); return {}; } template void HashMap::remove(const Key& key) { auto it = find(key); if (it != end()) remove(it); } template void HashMap::remove(iterator it) { it.outer_current()->remove(it.inner_current()); m_size--; } template void HashMap::clear() { m_buckets.clear(); m_size = 0; } template T& HashMap::operator[](const Key& key) { ASSERT(!empty()); auto& bucket = get_bucket(key); for (Entry& entry : bucket) if (entry.key == key) return entry.value; ASSERT_NOT_REACHED(); } template const T& HashMap::operator[](const Key& key) const { ASSERT(!empty()); const auto& bucket = get_bucket(key); for (const Entry& entry : bucket) if (entry.key == key) return entry.value; ASSERT_NOT_REACHED(); } template typename HashMap::iterator HashMap::find(const Key& key) { if (empty()) return end(); auto bucket_it = get_bucket_iterator(key); for (auto it = bucket_it->begin(); it != bucket_it->end(); it++) if (it->key == key) return iterator(m_buckets.end(), bucket_it, it); return end(); } template typename HashMap::const_iterator HashMap::find(const Key& key) const { if (empty()) return end(); auto bucket_it = get_bucket_iterator(key); for (auto it = bucket_it->begin(); it != bucket_it->end(); it++) if (it->key == key) return const_iterator(m_buckets.end(), bucket_it, it); return end(); } template bool HashMap::contains(const Key& key) const { return find(key) != end(); } template bool HashMap::empty() const { return m_size == 0; } template typename HashMap::size_type HashMap::size() const { return m_size; } template ErrorOr HashMap::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; TRY(new_buckets.resize(new_bucket_count)); for (auto& bucket : m_buckets) { for (auto it = bucket.begin(); it != bucket.end();) { size_type new_bucket_index = HASH()(it->key) % new_buckets.size(); it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it); } } m_buckets = move(new_buckets); return {}; } template LinkedList::Entry>& HashMap::get_bucket(const Key& key) { return *get_bucket_iterator(key); } template const LinkedList::Entry>& HashMap::get_bucket(const Key& key) const { return *get_bucket_iterator(key); } template Vector::Entry>>::iterator HashMap::get_bucket_iterator(const Key& key) { ASSERT(!m_buckets.empty()); auto index = HASH()(key) % m_buckets.size(); return next(m_buckets.begin(), index); } template Vector::Entry>>::const_iterator HashMap::get_bucket_iterator(const Key& key) const { ASSERT(!m_buckets.empty()); auto index = HASH()(key) % m_buckets.size(); return next(m_buckets.begin(), index); } }