#pragma once #include #include #include #include #include #include #include "openvic-simulation/utility/StringUtils.hpp" #include "openvic-simulation/utility/Utility.hpp" namespace OpenVic { struct ordered_container_string_hash { using is_transparent = void; [[nodiscard]] size_t operator()(char const* txt) const { return std::hash {}(txt); } [[nodiscard]] size_t operator()(std::string_view txt) const { return std::hash {}(txt); } [[nodiscard]] size_t operator()(std::string const& txt) const { return std::hash {}(txt); } }; template struct container_hash : std::hash {}; template<> struct container_hash : ordered_container_string_hash {}; template<> struct container_hash : ordered_container_string_hash {}; template<> struct container_hash : ordered_container_string_hash {}; // Useful for contiguous memory template< class Key, class T, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator>, class IndexType = std::uint_least32_t> using vector_ordered_map = tsl::ordered_map, Allocator>, IndexType>; // Useful for stable memory addresses (so long as you don't remove or insert values) template< class Key, class T, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator>, class IndexType = std::uint_least32_t> using deque_ordered_map = tsl::ordered_map, Allocator>, IndexType>; template< class Key, class T, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator>, class IndexType = std::uint_least32_t> using ordered_map = vector_ordered_map; // Useful for contiguous memory template< class Key, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator, class IndexType = std::uint_least32_t> using vector_ordered_set = tsl::ordered_set, IndexType>; // Useful for stable memory addresses (so long as you don't remove or insert values) template< class Key, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator, class IndexType = std::uint_least32_t> using deque_ordered_set = tsl::ordered_set, IndexType>; template< class Key, class Hash = container_hash, class KeyEqual = std::equal_to<>, class Allocator = std::allocator, class IndexType = std::uint_least32_t> using ordered_set = vector_ordered_set; template concept IsOrderedMap = utility::is_derived_from_specialization_of; template concept IsOrderedSet = utility::is_derived_from_specialization_of; template concept IsVectorOrderedMap = utility::is_derived_from_specialization_of; template concept IsVectorOrderedSet = utility::is_derived_from_specialization_of; template concept IsDequeOrderedMap = utility::is_derived_from_specialization_of; template concept IsDequeOrderedSet = utility::is_derived_from_specialization_of; template concept IsOrderedMapOf = IsOrderedMap && std::same_as && std::same_as; template concept IsOrderedSetOf = IsOrderedSet && std::same_as; template concept IsVectorOrderedMapOf = IsVectorOrderedMap && std::same_as && std::same_as; template concept IsVectorOrderedSetOf = IsVectorOrderedSet && std::same_as; template concept IsDequeOrderedMapOf = IsDequeOrderedMap && std::same_as && std::same_as; template concept IsDequeOrderedSetOf = IsDequeOrderedSet && std::same_as; /* Case-Insensitive Containers */ struct case_insensitive_string_hash { using is_transparent = void; private: /* Based on the byte array hashing functions in MSVC's . */ [[nodiscard]] static constexpr size_t _hash_bytes_case_insensitive(char const* first, size_t count) { constexpr size_t _offset_basis = 14695981039346656037ULL; constexpr size_t _prime = 1099511628211ULL; size_t hash = _offset_basis; for (size_t i = 0; i < count; ++i) { hash ^= static_cast(std::tolower(static_cast(first[i]))); hash *= _prime; } return hash; } public: [[nodiscard]] constexpr size_t operator()(char const* txt) const { return operator()(std::string_view { txt }); } [[nodiscard]] constexpr size_t operator()(std::string_view txt) const { return _hash_bytes_case_insensitive(txt.data(), txt.length()); } [[nodiscard]] constexpr size_t operator()(std::string const& txt) const { return _hash_bytes_case_insensitive(txt.data(), txt.length()); } }; struct case_insensitive_string_equal { using is_transparent = void; [[nodiscard]] constexpr bool operator()(std::string_view const& lhs, std::string_view const& rhs) const { return StringUtils::strings_equal_case_insensitive(lhs, rhs); } }; // Useful for contiguous memory template>, class IndexType = std::uint_least32_t> using case_insensitive_vector_ordered_map = vector_ordered_map; // Useful for stable memory addresses (so long as you don't remove or insert values) template>, class IndexType = std::uint_least32_t> using case_insensitive_deque_ordered_map = deque_ordered_map; template>, class IndexType = std::uint_least32_t> using case_insensitive_ordered_map = case_insensitive_vector_ordered_map; // Useful for contiguous memory template, class IndexType = std::uint_least32_t> using case_insensitive_vector_ordered_set = vector_ordered_set; // Useful for stable memory addresses (so long as you don't remove or insert values) template, class IndexType = std::uint_least32_t> using case_insensitive_deque_ordered_set = deque_ordered_set; template, class IndexType = std::uint_least32_t> using case_insensitive_ordered_set = case_insensitive_vector_ordered_set; template concept StringMapCase = requires(std::string_view identifier) { { typename Case::hash {}(identifier) } -> std::same_as; { typename Case::equal {}(identifier, identifier) } -> std::same_as; }; struct StringMapCaseSensitive { using hash = container_hash; using equal = std::equal_to<>; }; struct StringMapCaseInsensitive { using hash = case_insensitive_string_hash; using equal = case_insensitive_string_equal; }; /* Intermediate struct that "remembers" Case, instead of just decomposing it into its hash and equal components, * needed so that templates can deduce the Case with which a type was defined. */ template typename Container, StringMapCase Case, typename... Args> struct template_case_container_t : Container { using container_t = Container; using container_t::container_t; using case_t = Case; }; /* Template for map with string keys, supporting search by string_view without creating an intermediate string. */ template using template_string_map_t = template_case_container_t; template using string_map_t = template_string_map_t; template using case_insensitive_string_map_t = template_string_map_t; /* Template for set with string elements, supporting search by string_view without creating an intermediate string. */ template using template_string_set_t = template_case_container_t; using string_set_t = template_string_set_t; using case_insensitive_string_set_t = template_string_set_t; }