BAN: Rewrite HashMap as a wrapper around HashSet

There is really no need to have two implementation of the same thing.
Only difference now is that HashMap's value type has to be movable but
this wasn't an issue
This commit is contained in:
2026-04-21 00:18:18 +03:00
parent a8e496310b
commit 40f3546aca
3 changed files with 192 additions and 285 deletions

View File

@@ -1,49 +1,128 @@
#pragma once #pragma once
#include <BAN/Hash.h> #include <BAN/HashSet.h>
#include <BAN/LinkedList.h>
#include <BAN/Vector.h>
namespace BAN namespace BAN
{ {
template<typename Key, typename T, typename HASH = BAN::hash<Key>> template<typename HashSetIt, typename HashMap, typename Entry>
class HashMapIterator
{
public:
HashMapIterator() = default;
Entry& operator*()
{
return m_iterator.operator*();
}
const Entry& operator*() const
{
return m_iterator.operator*();
}
Entry* operator->()
{
return m_iterator.operator->();
}
const Entry* operator->() const
{
return m_iterator.operator->();
}
HashMapIterator& operator++()
{
++m_iterator;
return *this;
}
HashMapIterator operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
bool operator==(HashMapIterator other) const
{
return m_iterator == other.m_iterator;
}
bool operator!=(HashMapIterator other) const
{
return m_iterator != other.m_iterator;
}
private:
explicit HashMapIterator(HashSetIt it)
: m_iterator(it)
{ }
private:
HashSetIt m_iterator;
friend HashMap;
};
template<typename Key, typename T, typename HASH = BAN::hash<Key>, typename COMP = BAN::equal<Key>>
class HashMap class HashMap
{ {
public: public:
struct Entry struct Entry
{ {
template<typename... Args>
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
: key(key)
, value(forward<Args>(args)...)
{}
template<typename... Args>
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
: key(BAN::move(key))
, value(forward<Args>(args)...)
{}
Key key; Key key;
T value; T value;
}; };
struct EntryHash
{
constexpr bool operator()(const Key& a)
{
return HASH()(a);
}
constexpr bool operator()(const Entry& a)
{
return HASH()(a.key);
}
};
struct EntryComp
{
constexpr bool operator()(const Entry& a, const Key& b)
{
return COMP()(a.key, b);
}
constexpr bool operator()(const Entry& a, const Entry& b)
{
return COMP()(a.key, b.key);
}
};
public: public:
using size_type = size_t; using size_type = size_t;
using key_type = Key; using key_type = Key;
using value_type = T; using value_type = T;
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>; using iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::iterator, HashMap, Entry>;
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>; using const_iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::const_iterator, HashMap, const Entry>;
public: public:
HashMap() = default; HashMap() = default;
HashMap(const HashMap<Key, T, HASH>&); ~HashMap() { clear(); }
HashMap(HashMap<Key, T, HASH>&&);
~HashMap();
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&); HashMap(const HashMap<Key, T, HASH>& other) { *this = other; }
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&); HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>& other)
{
m_hash_set = other.m_hash_set;
return *this;
}
HashMap(HashMap<Key, T, HASH>&& other) { *this = BAN::move(other); }
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&& other)
{
m_hash_set = BAN::move(other.m_hash_set);
return *this;
}
iterator begin() { return iterator(m_hash_set.begin()); }
iterator end() { return iterator(m_hash_set.end()); }
const_iterator begin() const { return const_iterator(m_hash_set.begin()); }
const_iterator end() const { return const_iterator(m_hash_set.end()); }
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); } ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); } ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
@@ -57,263 +136,96 @@ namespace BAN
template<typename... Args> template<typename... Args>
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace(Key(key), forward<Args>(args)...); } { return emplace(Key(key), BAN::forward<Args>(args)...); }
template<typename... Args> template<typename... Args>
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<iterator> emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(!contains(key));
auto it = TRY(m_hash_set.insert(Entry { BAN::move(key), T(BAN::forward<Args>(args)...) }));
return iterator(it);
}
template<typename... Args> template<typename... Args>
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace_or_assign(Key(key), forward<Args>(args)...); } { return emplace_or_assign(Key(key), BAN::forward<Args>(args)...); }
template<typename... Args> template<typename... Args>
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<iterator> emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
if (auto it = m_hash_set.find(key); it != m_hash_set.end())
{
it->value = T(BAN::forward<Args>(args)...);
return iterator(it);
}
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); } auto it = TRY(m_hash_set.insert(Entry { BAN::move(key), T(BAN::forward<Args>(args)...) }));
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); } return iterator(it);
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<void> reserve(size_type); void remove(const Key& key)
{
if (auto it = find(key); it != end())
remove(it);
}
void remove(const Key&); iterator remove(iterator it)
void remove(iterator it); {
void clear(); return iterator(m_hash_set.remove(it.m_iterator));
}
T& operator[](const Key&); template<typename U> requires requires(const Key& a, const U& b) { COMP()(a, b); HASH()(b); }
const T& operator[](const Key&) const; iterator find(const U& key)
{
return iterator(m_hash_set.find(key));
}
iterator find(const Key& key); template<typename U> requires requires(const Key& a, const U& b) { COMP()(a, b); HASH()(b); }
const_iterator find(const Key& key) const; const_iterator find(const U& key) const
bool contains(const Key&) const; {
return const_iterator(m_hash_set.find(key));
}
bool empty() const; void clear()
size_type size() const; {
m_hash_set.clear();
}
ErrorOr<void> reserve(size_type size)
{
return m_hash_set.reserve(size);
}
T& operator[](const Key& key)
{
return find(key)->value;
}
const T& operator[](const Key& key) const
{
return find(key)->value;
}
bool contains(const Key& key) const
{
return find(key) != end();
}
size_type capacity() const
{
return m_hash_set.capacity();
}
size_type size() const
{
return m_hash_set.size();
}
bool empty() const
{
return m_hash_set.empty();
}
private: private:
ErrorOr<void> rebucket(size_type); HashSet<Entry, EntryHash, EntryComp> m_hash_set;
LinkedList<Entry>& get_bucket(const Key&);
const LinkedList<Entry>& get_bucket(const Key&) const;
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
private:
Vector<LinkedList<Entry>> m_buckets;
size_type m_size = 0;
friend iterator;
}; };
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
{
*this = other;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
{
*this = move(other);
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::~HashMap()
{
clear();
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
{
clear();
m_buckets = other.m_buckets;
m_size = other.m_size;
return *this;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
{
clear();
m_buckets = move(other.m_buckets);
m_size = other.m_size;
other.m_size = 0;
return *this;
}
template<typename Key, typename T, typename HASH>
template<typename... Args>
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(!contains(key));
TRY(rebucket(m_size + 1));
auto bucket_it = get_bucket_iterator(key);
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
m_size++;
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
}
template<typename Key, typename T, typename HASH>
template<typename... Args>
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
if (empty())
return emplace(move(key), forward<Args>(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>(args)...);
return iterator(m_buckets.end(), bucket_it, entry_it);
}
return emplace(move(key), forward<Args>(args)...);
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
{
TRY(rebucket(size));
return {};
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(const Key& key)
{
auto it = find(key);
if (it != end())
remove(it);
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(iterator it)
{
it.outer_current()->remove(it.inner_current());
m_size--;
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::clear()
{
m_buckets.clear();
m_size = 0;
}
template<typename Key, typename T, typename HASH>
T& HashMap<Key, T, HASH>::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<typename Key, typename T, typename HASH>
const T& HashMap<Key, T, HASH>::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 Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::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 Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::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<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::contains(const Key& key) const
{
return find(key) != end();
}
template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::empty() const
{
return m_size == 0;
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
{
return m_size;
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
{
if (m_buckets.size() >= bucket_count)
return {};
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<Entry>> 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<typename Key, typename T, typename HASH>
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
} }

View File

@@ -19,20 +19,14 @@
namespace LibInput namespace LibInput
{ {
struct StringViewLower struct StringViewLowerComp
{ {
BAN::StringView value; constexpr bool operator()(const BAN::StringView& a, const BAN::StringView& b) const
StringViewLower(BAN::StringView sv)
: value(sv)
{ }
bool operator==(const StringViewLower& other) const
{ {
if (value.size() != other.value.size()) if (a.size() != b.size())
return false; return false;
for (size_t i = 0; i < value.size(); i++) for (size_t i = 0; i < a.size(); i++)
if (tolower(value[i]) != tolower(other.value[i])) if (tolower(a[i]) != tolower(b[i]))
return false; return false;
return true; return true;
} }
@@ -40,16 +34,16 @@ namespace LibInput
struct StringViewLowerHash struct StringViewLowerHash
{ {
BAN::hash_t operator()(const StringViewLower& value) const BAN::hash_t operator()(const BAN::StringView& str) const
{ {
constexpr BAN::hash_t FNV_offset_basis = 0x811c9dc5; constexpr BAN::hash_t FNV_offset_basis = 0x811c9dc5;
constexpr BAN::hash_t FNV_prime = 0x01000193; constexpr BAN::hash_t FNV_prime = 0x01000193;
BAN::hash_t hash = FNV_offset_basis; BAN::hash_t hash = FNV_offset_basis;
for (size_t i = 0; i < value.value.size(); i++) for (size_t i = 0; i < str.size(); i++)
{ {
hash *= FNV_prime; hash *= FNV_prime;
hash ^= (uint8_t)tolower(value.value[i]); hash ^= (uint8_t)tolower(str[i]);
} }
return hash; return hash;
@@ -113,7 +107,7 @@ namespace LibInput
return keycode; return keycode;
} }
static BAN::HashMap<StringViewLower, Key, StringViewLowerHash> s_name_to_key; static BAN::HashMap<BAN::StringView, Key, StringViewLowerHash, StringViewLowerComp> s_name_to_key;
static BAN::ErrorOr<void> initialize_name_to_key(); static BAN::ErrorOr<void> initialize_name_to_key();
static BAN::Optional<Key> parse_key(BAN::StringView name) static BAN::Optional<Key> parse_key(BAN::StringView name)

View File

@@ -3,6 +3,7 @@
#include <BAN/Endianness.h> #include <BAN/Endianness.h>
#include <BAN/HashMap.h> #include <BAN/HashMap.h>
#include <BAN/IPv4.h> #include <BAN/IPv4.h>
#include <BAN/LinkedList.h>
#include <BAN/String.h> #include <BAN/String.h>
#include <BAN/StringView.h> #include <BAN/StringView.h>
#include <BAN/Vector.h> #include <BAN/Vector.h>