From e0c16b772cebf9b4749933dff2c289d48581164a Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 16 May 2022 16:36:52 +0200 Subject: [PATCH] Adds new StringViewDictionary with specialized methods to retrieve values by the hash of the StringView --- src/Collections/StringViewDictionary.hpp | 141 +++++++++++++++++++++++ tests/DictionaryTests.cpp | 11 ++ 2 files changed, 152 insertions(+) create mode 100644 src/Collections/StringViewDictionary.hpp diff --git a/src/Collections/StringViewDictionary.hpp b/src/Collections/StringViewDictionary.hpp new file mode 100644 index 0000000..101a9b3 --- /dev/null +++ b/src/Collections/StringViewDictionary.hpp @@ -0,0 +1,141 @@ +#ifndef ARBUTILS_STRINGVIEWDICTIONARY_HPP +#define ARBUTILS_STRINGVIEWDICTIONARY_HPP +#include +#include +#include +#include +#include "../Ensure.hpp" +#include "../StringView.hpp" + +namespace ArbUt { + /// @brief Wrapper around unordered_map, allowing safer access and adding several helper methods. + template class StringViewDictionary { + private: + + struct StringViewHash + { + using hash_type = std::hash; + using is_transparent = void; + + size_t operator()(const char* str) const { return hash_type{}(str); } + size_t operator()(StringView const& str) const { return hash_type{}(str); } + size_t operator()(u32 hash) const { return hash; } + }; + + std::unordered_map> _map; + + using iterator = typename std::unordered_map::iterator; + using const_iterator = typename std::unordered_map::const_iterator; + + public: + /// @brief The type used for the indexer of the dictionary. + typedef StringView keyType; + /// @brief The type used for the value in the dictionary. + typedef ValueT valueType; + + StringViewDictionary() : _map() {} + /// @brief Initialises a dictionary with a certain capacity. + explicit StringViewDictionary(size_t capacity) : _map(capacity) {} + /// @brief Initialises a dictionary from an initializer_list. + StringViewDictionary(const std::initializer_list>& l) : _map(l) {} + /// @brief Copy operator + StringViewDictionary(const StringViewDictionary& other) noexcept : _map(other._map) {} + /// @brief Assignment operator + StringViewDictionary& operator=(const StringViewDictionary& other) noexcept { + if (this == &other) { + return *this; + } + _map = other._map; + return *this; + } + + /// @brief Removes all items from the dictionary. + inline void Clear() noexcept { _map.clear(); } + + /// @brief Inserts a new item in the dictionary. This will throw if the dictionary already contains the key. + inline void Insert(const StringView& key, const ValueT& value) { + [[maybe_unused]] const auto& v = _map.insert({key, value}); +#ifndef NO_ASSERT + if (!v.second) + throw ArbUt::Exception("Key already exists"); +#endif + } + + /// @brief Sets a key in the dictionary to a specific value. + inline void Set(const StringView& key, const ValueT& value) { _map[key] = value; } + + /// @brief Gets a value from the dictionary. + [[nodiscard]] inline ValueT& Get(const StringView& key) { +#ifndef NO_ASSERT + return _map.at(key); +#else + return _map[key]; +#endif + } + /// @brief Gets a value from the dictionary. + [[nodiscard]] inline const ValueT& Get(const StringView& key) const { return _map.at(key); } + + /// @brief Gets a value from the dictionary using the direct hash of the key. + [[nodiscard]] inline ValueT& GetFromHash(u32 hash) { +#ifndef NO_ASSERT + return _map.find(hash)->second; +#else + return _map[key]; +#endif + } + /// @brief Gets a value from the dictionary using the direct hash of the key. + [[nodiscard]] inline const ValueT& GetFromHash(u32 hash) const { return _map.find(hash)->second; } + + + /// @brief Try to get an item from the dictionary using a key. Returns false if no item is found, and out will + /// not be touched in that case. + inline std::optional> TryGet(const StringView& key) const noexcept { + const auto& find = _map.find(key); + if (find == _map.end()) { + return {}; + } + return std::ref(find->second); + } + + /// @brief Try to get an item from the dictionary using the hash of a key. Returns false if no item is found, and out will + /// not be touched in that case. + inline std::optional> TryGet(u32 hash) const noexcept { + const auto& find = _map.find(hash); + if (find == _map.end()) { + return {}; + } + return std::ref(find->second); + } + + /// @brief Removes an item with a certain key from the dictionary + inline void Remove(const StringView& key) { _map.erase(key); } + + /// @brief Returns the number of items in the dictionary. + [[nodiscard]] inline size_t Count() const noexcept { return _map.size(); } + + /// @brief Checks whether the dictionary contains a specific key. + inline bool Has(const StringView& key) const noexcept { return _map.find(key) != _map.end(); } + + /// @brief Indexing operator to get a value from the dictionary using a key. + inline ValueT& operator[](const StringView& key) { return Get(key); } + /// @brief Indexing operator to get a value from the dictionary using a key. + inline const ValueT& operator[](const StringView& key) const { return Get(key); } + + /// @brief returns an iterator to the beginning of the specified bucket + iterator begin() noexcept { return _map.begin(); } + /// @brief returns an iterator to the beginning of the specified bucket + const_iterator begin() const noexcept { return _map.begin(); } + + /// @brief returns an iterator to the end of the specified bucket + iterator end() noexcept { return _map.end(); } + /// @brief returns an iterator to the end of the specified bucket + const_iterator end() const { return _map.end(); } + + /// @brief returns the backing unordered_map. + const std::unordered_map& GetStdMap() const noexcept { return _map; } + /// @brief returns the backing unordered_map. + std::unordered_map& GetStdMap() noexcept { return _map; } + }; +} + +#endif // ARBUTILS_STRINGVIEWDICTIONARY_HPP diff --git a/tests/DictionaryTests.cpp b/tests/DictionaryTests.cpp index 45e5e78..45229cb 100644 --- a/tests/DictionaryTests.cpp +++ b/tests/DictionaryTests.cpp @@ -1,6 +1,7 @@ #ifdef TESTS_BUILD #include #include "../src/Collections/Dictionary.hpp" +#include "../src/Collections/StringViewDictionary.hpp" using namespace ArbUt; TEST_CASE("Create Dictionary, insert values") { @@ -107,4 +108,14 @@ TEST_CASE("Create Dictionary with different types, insert values, iterate over k CHECK(i == 3); } + +TEST_CASE("Create StringViewDictionary, insert values, get values by hash") { + auto dic = StringViewDictionary(5); + dic.Insert("foo"_cnc, 5); + dic.Insert("bar"_cnc, 100); + dic.Insert("zet"_cnc, 2000); + + CHECK(dic.GetFromHash("bar"_cnc) == 100); +} + #endif \ No newline at end of file