libluna: Document HashMap and HashTable
This commit is contained in:
parent
cbea66c533
commit
d48142f163
@ -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();
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user