2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @file HashMap.h
|
|
|
|
* @author apio (cloudapio.eu)
|
|
|
|
* @brief Map between keys and values with best-case constant time lookup.
|
|
|
|
*
|
|
|
|
* @copyright Copyright (c) 2023, the Luna authors.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2023-06-16 22:07:43 +00:00
|
|
|
#pragma once
|
|
|
|
#include <luna/HashTable.h>
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Internal representation of a key-value pair in a HashMap.
|
|
|
|
*
|
|
|
|
* @tparam K The key type.
|
|
|
|
* @tparam V The value type.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
template <typename K, typename V> struct HashPair
|
|
|
|
{
|
|
|
|
K key;
|
|
|
|
Option<V> value;
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Compare two HashPair objects's keys.
|
|
|
|
*
|
|
|
|
* @param other The HashPair to compare against.
|
|
|
|
* @return true The keys match.
|
|
|
|
* @return false The keys do not match.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
bool operator==(const HashPair<K, V>& other) const
|
|
|
|
{
|
|
|
|
return key == other.key;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Template specialization of hash() for HashPair objects.
|
|
|
|
*
|
|
|
|
* This function hashes only the key, ignoring the value.
|
|
|
|
*
|
|
|
|
* @tparam K The key type.
|
|
|
|
* @tparam V The value type.
|
|
|
|
* @param value The HashPair to hash.
|
|
|
|
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
|
|
|
|
* @return u64 The calculated hash.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt)
|
|
|
|
{
|
|
|
|
return hash(value.key, salt);
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief A map between keys and values with best-case constant time lookup.
|
|
|
|
*
|
|
|
|
* @tparam K The key type.
|
|
|
|
* @tparam V The value type.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
template <typename K, typename V> struct HashMap
|
|
|
|
{
|
|
|
|
public:
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Try to insert a key-value pair into the HashMap.
|
|
|
|
*
|
|
|
|
* @param key The key to use.
|
|
|
|
* @param value The value to use.
|
|
|
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
|
|
|
|
*/
|
2023-08-02 20:18:36 +00:00
|
|
|
Result<bool> try_set(const K& key, V&& value)
|
|
|
|
{
|
|
|
|
return m_table.try_set(HashPair<K, V> { key, move(value) });
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Try to insert a key-value pair into the HashMap.
|
|
|
|
*
|
|
|
|
* @param key The key to use.
|
|
|
|
* @param value The value to use.
|
|
|
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
Result<bool> try_set(const K& key, const V& value)
|
|
|
|
{
|
|
|
|
return m_table.try_set(HashPair<K, V> { key, value });
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Get the associated value for a key.
|
|
|
|
*
|
|
|
|
* @param key The key to use.
|
|
|
|
* @return Option<V> The associated value, or an empty option if the key did not exist.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
Option<V> try_get(const K& key)
|
|
|
|
{
|
|
|
|
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
|
|
|
if (!p) return {};
|
|
|
|
return p->value;
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Get a pointer to the associated value for a key inside the HashMap.
|
|
|
|
*
|
|
|
|
* This pointer should always be checked before usage.
|
|
|
|
*
|
|
|
|
* @param key The key to use.
|
|
|
|
* @return V* A pointer to the associated value, or nullptr if the key did not exist.
|
|
|
|
*/
|
2023-06-21 19:32:28 +00:00
|
|
|
V* try_get_ref(const K& key)
|
|
|
|
{
|
|
|
|
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
|
|
|
if (!p) return nullptr;
|
|
|
|
return p->value.value_ptr();
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Remove a key-value pair from this HashMap.
|
|
|
|
*
|
|
|
|
* @param key The key to use.
|
|
|
|
* @return true The pair was successfully removed.
|
|
|
|
* @return false The key did not exist.
|
|
|
|
*/
|
2023-06-16 22:07:43 +00:00
|
|
|
bool try_remove(const K& key)
|
|
|
|
{
|
|
|
|
return m_table.try_remove(HashPair<K, V> { key, {} });
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Return the number of key-value pairs that can currently fit in the HashMap.
|
|
|
|
*
|
|
|
|
* The number of actual entries will always be smaller than this (unless it is 0), since the HashMap grows before
|
|
|
|
* the number of entries reaches the capacity.
|
|
|
|
*
|
|
|
|
* @return usize The current capacity.
|
|
|
|
*/
|
2023-08-17 18:14:33 +00:00
|
|
|
usize capacity() const
|
|
|
|
{
|
|
|
|
return m_table.capacity();
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Return the number of key-value pairs currently contained in this HashMap.
|
|
|
|
*
|
|
|
|
* @return usize The number of pairs contained.
|
|
|
|
*/
|
2023-08-17 18:14:33 +00:00
|
|
|
usize size() const
|
|
|
|
{
|
|
|
|
return m_table.size();
|
|
|
|
}
|
|
|
|
|
2023-08-26 18:50:12 +00:00
|
|
|
/**
|
|
|
|
* @brief Clear the HashMap.
|
|
|
|
*/
|
2023-08-16 12:54:13 +00:00
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
m_table.clear();
|
|
|
|
}
|
|
|
|
|
2024-07-02 18:51:28 +00:00
|
|
|
const HashTable<HashPair<K, V>>::HashTableIterator begin()
|
|
|
|
{
|
|
|
|
return m_table.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
const HashTable<HashPair<K, V>>::HashTableIterator end()
|
|
|
|
{
|
|
|
|
return m_table.end();
|
|
|
|
}
|
|
|
|
|
2023-06-16 22:07:43 +00:00
|
|
|
private:
|
|
|
|
HashTable<HashPair<K, V>> m_table;
|
|
|
|
};
|