libluna: Document HashMap and HashTable

This commit is contained in:
apio 2023-08-26 20:50:12 +02:00
parent cbea66c533
commit d48142f163
Signed by: apio
GPG Key ID: B8A7D06E42258954
2 changed files with 147 additions and 2 deletions

View File

@ -1,35 +1,94 @@
/**
* @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.
*
*/
#pragma once #pragma once
#include <luna/HashTable.h> #include <luna/HashTable.h>
/**
* @brief Internal representation of a key-value pair in a HashMap.
*
* @tparam K The key type.
* @tparam V The value type.
*/
template <typename K, typename V> struct HashPair template <typename K, typename V> struct HashPair
{ {
K key; K key;
Option<V> value; Option<V> value;
/**
* @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.
*/
bool operator==(const HashPair<K, V>& other) const bool operator==(const HashPair<K, V>& other) const
{ {
return key == other.key; return key == other.key;
} }
}; };
/**
* @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.
*/
template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt) template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt)
{ {
return hash(value.key, salt); return hash(value.key, salt);
} }
/**
* @brief A map between keys and values with best-case constant time lookup.
*
* @tparam K The key type.
* @tparam V The value type.
*/
template <typename K, typename V> struct HashMap template <typename K, typename V> struct HashMap
{ {
public: public:
/**
* @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.
*/
Result<bool> try_set(const K& key, V&& value) Result<bool> try_set(const K& key, V&& value)
{ {
return m_table.try_set(HashPair<K, V> { key, move(value) }); return m_table.try_set(HashPair<K, V> { key, move(value) });
} }
/**
* @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.
*/
Result<bool> try_set(const K& key, const V& value) Result<bool> try_set(const K& key, const V& value)
{ {
return m_table.try_set(HashPair<K, V> { key, value }); return m_table.try_set(HashPair<K, V> { key, value });
} }
/**
* @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.
*/
Option<V> try_get(const K& key) Option<V> try_get(const K& key)
{ {
auto* p = m_table.try_find(HashPair<K, V> { key, {} }); auto* p = m_table.try_find(HashPair<K, V> { key, {} });
@ -37,6 +96,14 @@ template <typename K, typename V> struct HashMap
return p->value; return p->value;
} }
/**
* @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.
*/
V* try_get_ref(const K& key) V* try_get_ref(const K& key)
{ {
auto* p = m_table.try_find(HashPair<K, V> { key, {} }); auto* p = m_table.try_find(HashPair<K, V> { key, {} });
@ -44,21 +111,44 @@ template <typename K, typename V> struct HashMap
return p->value.value_ptr(); return p->value.value_ptr();
} }
/**
* @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.
*/
bool try_remove(const K& key) bool try_remove(const K& key)
{ {
return m_table.try_remove(HashPair<K, V> { key, {} }); return m_table.try_remove(HashPair<K, V> { key, {} });
} }
/**
* @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.
*/
usize capacity() const usize capacity() const
{ {
return m_table.capacity(); return m_table.capacity();
} }
/**
* @brief Return the number of key-value pairs currently contained in this HashMap.
*
* @return usize The number of pairs contained.
*/
usize size() const usize size() const
{ {
return m_table.size(); return m_table.size();
} }
/**
* @brief Clear the HashMap.
*/
void clear() void clear()
{ {
m_table.clear(); m_table.clear();

View File

@ -1,23 +1,49 @@
/**
* @file HashTable.h
* @author apio (cloudapio.eu)
* @brief Table of values with best-case constant time lookup.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/Hash.h> #include <luna/Hash.h>
#include <luna/Heap.h> #include <luna/Heap.h>
#include <luna/Option.h> #include <luna/Option.h>
/**
* @brief A table of values with best-case constant time lookup.
*
* @tparam T The type of values to store.
*/
template <typename T> class HashTable template <typename T> class HashTable
{ {
static constexpr usize GROW_RATE = 2; static constexpr usize GROW_RATE = 2;
static constexpr usize GROW_FACTOR = 16; static constexpr usize GROW_FACTOR = 2;
public: public:
/**
* @brief Try to insert a value into the table.
*
* @param value The value to insert.
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
*/
Result<bool> try_set(const T& value) Result<bool> try_set(const T& value)
{ {
T copy { value }; T copy { value };
return try_set(move(copy)); return try_set(move(copy));
} }
/**
* @brief Try to insert a value into the table.
*
* @param value The value to insert.
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
*/
Result<bool> try_set(T&& value) Result<bool> try_set(T&& value)
{ {
if (should_grow()) TRY(rehash(m_capacity + GROW_FACTOR)); if (should_grow()) TRY(rehash(m_capacity * GROW_FACTOR));
u64 index = hash(value, m_salt) % m_capacity; u64 index = hash(value, m_salt) % m_capacity;
@ -45,6 +71,12 @@ template <typename T> class HashTable
return true; return true;
} }
/**
* @brief Find a value inside the table, returning a pointer to it if found.
*
* @param value The value to compare against.
* @return T* A pointer to the value inside the table if found, or nullptr if it was not found.
*/
T* try_find(const T& value) T* try_find(const T& value)
{ {
if (!m_size) return nullptr; if (!m_size) return nullptr;
@ -69,6 +101,13 @@ template <typename T> class HashTable
return nullptr; return nullptr;
} }
/**
* @brief Remove a value from this table.
*
* @param value The value to remove.
* @return true The value was successfully removed.
* @return false The value did not exist.
*/
bool try_remove(const T& value) bool try_remove(const T& value)
{ {
if (!m_size) return false; if (!m_size) return false;
@ -99,6 +138,9 @@ template <typename T> class HashTable
return false; return false;
} }
/**
* @brief Clear the table.
*/
void clear() void clear()
{ {
if (m_capacity) if (m_capacity)
@ -110,11 +152,24 @@ template <typename T> class HashTable
} }
} }
/**
* @brief Return the number of values that can currently fit in the table.
*
* The number of actual entries will always be smaller than this (unless it is 0), since the table grows before
* the number of entries reaches the capacity.
*
* @return usize The current capacity.
*/
usize capacity() const usize capacity() const
{ {
return m_capacity; return m_capacity;
} }
/**
* @brief Return the number of values currently contained in the table.
*
* @return usize The number of values contained.
*/
usize size() const usize size() const
{ {
return m_size; return m_size;