/** * @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, {} }); if (!p) return {}; 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, {} }); if (!p) return nullptr; 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(); } private: HashTable> m_table; };