diff --git a/BAN/include/BAN/HashMap.h b/BAN/include/BAN/HashMap.h index 38b3afba0a..061a73e972 100644 --- a/BAN/include/BAN/HashMap.h +++ b/BAN/include/BAN/HashMap.h @@ -7,10 +7,7 @@ namespace BAN { - template - class HashMapIterator; - - template> + template, bool STABLE = true> class HashMap { public: @@ -35,12 +32,12 @@ namespace BAN public: HashMap() = default; - HashMap(const HashMap&); - HashMap(HashMap&&); + HashMap(const HashMap&); + HashMap(HashMap&&); ~HashMap(); - HashMap& operator=(const HashMap&); - HashMap& operator=(HashMap&&); + HashMap& operator=(const HashMap&); + HashMap& operator=(HashMap&&); ErrorOr insert(const Key&, const T&); ErrorOr insert(const Key&, T&&); @@ -77,26 +74,26 @@ namespace BAN friend iterator; }; - template - HashMap::HashMap(const HashMap& other) + template + HashMap::HashMap(const HashMap& other) { *this = other; } - template - HashMap::HashMap(HashMap&& other) + template + HashMap::HashMap(HashMap&& other) { *this = move(other); } - template - HashMap::~HashMap() + template + HashMap::~HashMap() { clear(); } - template - HashMap& HashMap::operator=(const HashMap& other) + template + HashMap& HashMap::operator=(const HashMap& other) { clear(); m_buckets = other.m_buckets; @@ -104,8 +101,8 @@ namespace BAN return *this; } - template - HashMap& HashMap::operator=(HashMap&& other) + template + HashMap& HashMap::operator=(HashMap&& other) { clear(); m_buckets = move(other.m_buckets); @@ -114,21 +111,21 @@ namespace BAN return *this; } - template - ErrorOr HashMap::insert(const Key& key, const T& value) + template + ErrorOr HashMap::insert(const Key& key, const T& value) { return insert(key, move(T(value))); } - template - ErrorOr HashMap::insert(const Key& key, T&& value) + template + ErrorOr HashMap::insert(const Key& key, T&& value) { return emplace(key, move(value)); } - template + template template - ErrorOr HashMap::emplace(const Key& key, Args&&... args) + ErrorOr HashMap::emplace(const Key& key, Args&&... args) { ASSERT(!contains(key)); TRY(rebucket(m_size + 1)); @@ -140,15 +137,15 @@ namespace BAN return {}; } - template - ErrorOr HashMap::reserve(size_type size) + template + ErrorOr HashMap::reserve(size_type size) { TRY(rebucket(size)); return {}; } - template - void HashMap::remove(const Key& key) + template + void HashMap::remove(const Key& key) { if (empty()) return; auto& bucket = get_bucket(key); @@ -163,15 +160,15 @@ namespace BAN } } - template - void HashMap::clear() + template + void HashMap::clear() { m_buckets.clear(); m_size = 0; } - template - T& HashMap::operator[](const Key& key) + template + T& HashMap::operator[](const Key& key) { ASSERT(!empty()); auto& bucket = get_bucket(key); @@ -181,8 +178,8 @@ namespace BAN ASSERT(false); } - template - const T& HashMap::operator[](const Key& key) const + template + const T& HashMap::operator[](const Key& key) const { ASSERT(!empty()); const auto& bucket = get_bucket(key); @@ -192,8 +189,8 @@ namespace BAN ASSERT(false); } - template - bool HashMap::contains(const Key& key) const + template + bool HashMap::contains(const Key& key) const { if (empty()) return false; const auto& bucket = get_bucket(key); @@ -203,20 +200,20 @@ namespace BAN return false; } - template - bool HashMap::empty() const + template + bool HashMap::empty() const { return m_size == 0; } - template - typename HashMap::size_type HashMap::size() const + template + typename HashMap::size_type HashMap::size() const { return m_size; } - template - ErrorOr HashMap::rebucket(size_type bucket_count) + template + ErrorOr HashMap::rebucket(size_type bucket_count) { if (m_buckets.size() >= bucket_count) return {}; @@ -225,16 +222,21 @@ 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 entries to the new entries and not move - // since we might run out of memory half way through. - for (auto& bucket : m_buckets) + + if constexpr(STABLE) { - for (Entry& entry : bucket) + // NOTE: We have to copy the old entries to the new entries and not move + // since we might run out of memory half way through. + for (auto& bucket : m_buckets) { - size_type bucket_index = HASH()(entry.key) % new_buckets.size(); - if (new_buckets[bucket_index].push_back(entry).is_error()) - return Error::from_errno(ENOMEM); + for (Entry& entry : bucket) + { + size_type bucket_index = HASH()(entry.key) % new_buckets.size(); + if constexpr(STABLE) + TRY(new_buckets[bucket_index].push_back(entry)); + else + TRY(new_buckets[bucket_index].push_back(BAN::move(entry))); + } } } @@ -242,20 +244,27 @@ namespace BAN return {}; } - template - LinkedList::Entry>& HashMap::get_bucket(const Key& key) + template + LinkedList::Entry>& HashMap::get_bucket(const Key& key) { ASSERT(!m_buckets.empty()); auto index = HASH()(key) % m_buckets.size(); return m_buckets[index]; } - template - const LinkedList::Entry>& HashMap::get_bucket(const Key& key) const + template + const LinkedList::Entry>& HashMap::get_bucket(const Key& key) const { ASSERT(!m_buckets.empty()); auto index = HASH()(key) % m_buckets.size(); return m_buckets[index]; } + // Unstable hash map moves values between container during rebucketing. + // This means that if insertion to map 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 map after insertion fails. + template> + using HashMapUnstable = HashMap; + }