diff --git a/libluna/include/luna/HashMap.h b/libluna/include/luna/HashMap.h index 6a360d5f..e2573b01 100644 --- a/libluna/include/luna/HashMap.h +++ b/libluna/include/luna/HashMap.h @@ -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 #include +/** + * @brief Internal representation of a key-value pair in a HashMap. + * + * @tparam K The key type. + * @tparam V The value type. + */ template struct HashPair { K key; Option 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& other) const { 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 u64 hash(const HashPair& value, u64 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 struct HashMap { 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 An error, true if the insertion succeeded, or false if the key already existed. + */ Result try_set(const K& key, V&& value) { return m_table.try_set(HashPair { 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 An error, true if the insertion succeeded, or false if the key already existed. + */ Result try_set(const K& key, const V& value) { return m_table.try_set(HashPair { key, value }); } + /** + * @brief Get the associated value for a key. + * + * @param key The key to use. + * @return Option The associated value, or an empty option if the key did not exist. + */ Option try_get(const K& key) { auto* p = m_table.try_find(HashPair { key, {} }); @@ -37,6 +96,14 @@ template struct HashMap 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) { auto* p = m_table.try_find(HashPair { key, {} }); @@ -44,21 +111,44 @@ template struct HashMap 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) { return m_table.try_remove(HashPair { 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 { 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 { return m_table.size(); } + /** + * @brief Clear the HashMap. + */ void clear() { m_table.clear(); diff --git a/libluna/include/luna/HashTable.h b/libluna/include/luna/HashTable.h index 1a366800..18e255bb 100644 --- a/libluna/include/luna/HashTable.h +++ b/libluna/include/luna/HashTable.h @@ -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 #include #include #include +/** + * @brief A table of values with best-case constant time lookup. + * + * @tparam T The type of values to store. + */ template class HashTable { static constexpr usize GROW_RATE = 2; - static constexpr usize GROW_FACTOR = 16; + static constexpr usize GROW_FACTOR = 2; public: + /** + * @brief Try to insert a value into the table. + * + * @param value The value to insert. + * @return Result An error, true if the insertion succeeded, or false if the value already existed. + */ Result try_set(const T& value) { T copy { value }; return try_set(move(copy)); } + /** + * @brief Try to insert a value into the table. + * + * @param value The value to insert. + * @return Result An error, true if the insertion succeeded, or false if the value already existed. + */ Result 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; @@ -45,6 +71,12 @@ template class HashTable 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) { if (!m_size) return nullptr; @@ -69,6 +101,13 @@ template class HashTable 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) { if (!m_size) return false; @@ -99,6 +138,9 @@ template class HashTable return false; } + /** + * @brief Clear the table. + */ void clear() { if (m_capacity) @@ -110,11 +152,24 @@ template 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 { return m_capacity; } + /** + * @brief Return the number of values currently contained in the table. + * + * @return usize The number of values contained. + */ usize size() const { return m_size;