From b09f1d162f045fd796ac215823a075dfb0518606 Mon Sep 17 00:00:00 2001 From: Julien Jerphanion Date: Mon, 29 Jul 2024 11:06:45 +0200 Subject: [PATCH] Make more classes hashable and comparable (#3363) Signed-off-by: Julien Jerphanion Co-authored-by: Klaim --- .../include/mamba/specs/build_number_spec.hpp | 21 +++++ .../mamba/specs/chimera_string_spec.hpp | 21 +++++ libmamba/include/mamba/specs/glob_spec.hpp | 20 +++++ libmamba/include/mamba/specs/match_spec.hpp | 82 +++++++++++++++++++ libmamba/include/mamba/specs/package_info.hpp | 44 ++++++++++ libmamba/include/mamba/specs/regex_spec.hpp | 12 +++ .../mamba/specs/unresolved_channel.hpp | 22 +++++ libmamba/include/mamba/specs/version_spec.hpp | 30 +++++++ .../include/mamba/util/flat_binary_tree.hpp | 24 ++++++ .../mamba/util/flat_bool_expr_tree.hpp | 11 +++ libmamba/include/mamba/util/flat_set.hpp | 12 +++ libmamba/include/mamba/util/heap_optional.hpp | 28 +++++++ 12 files changed, 327 insertions(+) diff --git a/libmamba/include/mamba/specs/build_number_spec.hpp b/libmamba/include/mamba/specs/build_number_spec.hpp index b9e0533e9d..695d3f196f 100644 --- a/libmamba/include/mamba/specs/build_number_spec.hpp +++ b/libmamba/include/mamba/specs/build_number_spec.hpp @@ -15,6 +15,7 @@ #include #include "mamba/specs/error.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -124,6 +125,17 @@ namespace mamba::specs */ [[nodiscard]] auto contains(BuildNumber point) const -> bool; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const BuildNumberSpec& other) const -> bool + { + return m_predicate == other.m_predicate; + } + + [[nodiscard]] auto operator!=(const BuildNumberSpec& other) const -> bool + { + return !(*this == other); + } + private: BuildNumberPredicate m_predicate; @@ -155,4 +167,13 @@ struct fmt::formatter format(const ::mamba::specs::BuildNumberSpec& spec, format_context& ctx) -> decltype(ctx.out()); }; +template <> +struct std::hash +{ + auto operator()(const mamba::specs::BuildNumberSpec& spec) const -> std::size_t + { + return mamba::util::hash_vals(spec.str()); + } +}; + #endif diff --git a/libmamba/include/mamba/specs/chimera_string_spec.hpp b/libmamba/include/mamba/specs/chimera_string_spec.hpp index e1cfbb7e15..c2f3112dd7 100644 --- a/libmamba/include/mamba/specs/chimera_string_spec.hpp +++ b/libmamba/include/mamba/specs/chimera_string_spec.hpp @@ -15,6 +15,7 @@ #include "mamba/specs/error.hpp" #include "mamba/specs/glob_spec.hpp" #include "mamba/specs/regex_spec.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -51,6 +52,17 @@ namespace mamba::specs [[nodiscard]] auto str() const -> const std::string&; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const ChimeraStringSpec& other) const -> bool + { + return m_spec == other.m_spec; + } + + [[nodiscard]] auto operator!=(const ChimeraStringSpec& other) const -> bool + { + return !(*this == other); + } + private: Chimera m_spec; @@ -66,4 +78,13 @@ struct fmt::formatter format(const ::mamba::specs::ChimeraStringSpec& spec, format_context& ctx) -> decltype(ctx.out()); }; +template <> +struct std::hash +{ + auto operator()(const mamba::specs::ChimeraStringSpec& spec) const -> std::size_t + { + return mamba::util::hash_vals(spec.str()); + } +}; + #endif diff --git a/libmamba/include/mamba/specs/glob_spec.hpp b/libmamba/include/mamba/specs/glob_spec.hpp index 71d8411453..f4090f779d 100644 --- a/libmamba/include/mamba/specs/glob_spec.hpp +++ b/libmamba/include/mamba/specs/glob_spec.hpp @@ -43,6 +43,17 @@ namespace mamba::specs [[nodiscard]] auto str() const -> const std::string&; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const GlobSpec& other) const -> bool + { + return m_pattern == other.m_pattern; + } + + [[nodiscard]] auto operator!=(const GlobSpec& other) const -> bool + { + return !(*this == other); + } + private: std::string m_pattern = std::string(free_pattern); @@ -57,4 +68,13 @@ struct fmt::formatter auto format(const ::mamba::specs::GlobSpec& spec, format_context& ctx) -> decltype(ctx.out()); }; +template <> +struct std::hash +{ + auto operator()(const mamba::specs::GlobSpec& spec) const -> std::size_t + { + return std::hash{}(spec.str()); + } +}; + #endif diff --git a/libmamba/include/mamba/specs/match_spec.hpp b/libmamba/include/mamba/specs/match_spec.hpp index 52108f295c..1d690bacb1 100644 --- a/libmamba/include/mamba/specs/match_spec.hpp +++ b/libmamba/include/mamba/specs/match_spec.hpp @@ -22,6 +22,7 @@ #include "mamba/specs/version_spec.hpp" #include "mamba/util/flat_set.hpp" #include "mamba/util/heap_optional.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -135,6 +136,28 @@ namespace mamba::specs */ [[nodiscard]] auto contains_except_channel(const PackageInfo& pkg) const -> bool; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const MatchSpec& other) const -> bool + { + return m_channel == other.m_channel // + && m_version == other.m_version // + && m_name == other.m_name // + && m_build_string == other.m_build_string // + && m_name_space == other.m_name_space // + && m_build_number == other.m_build_number // + && m_extra == other.m_extra; + } + + [[nodiscard]] auto operator!=(const MatchSpec& other) const -> bool + { + return !(*this == other); + } + + auto extra_members_hash() const -> std::size_t + { + return mamba::util::hash_vals(m_extra); + } + private: struct ExtraMembers @@ -150,7 +173,29 @@ namespace mamba::specs std::string features = {}; string_set track_features = {}; bool optional = false; + + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const ExtraMembers& other) const -> bool + { + return filename == other.filename // + && subdirs == other.subdirs // + && md5 == other.md5 // + && sha256 == other.sha256 // + && license == other.license // + && license_family == other.license_family // + && features == other.features // + && track_features == other.track_features // + && optional == other.optional; + } + + [[nodiscard]] auto operator!=(const ExtraMembers& other) const -> bool + { + return !(*this == other); + } + + friend struct std::hash; }; + friend struct std::hash; std::optional m_channel; VersionSpec m_version; @@ -255,4 +300,41 @@ namespace mamba::specs return true; } } + +template <> +struct std::hash +{ + auto operator()(const mamba::specs::MatchSpec& spec) const -> std::size_t + { + return mamba::util::hash_vals( + spec.channel(), + spec.version(), + spec.name(), + spec.build_string(), + spec.name_space(), + spec.build_number(), + spec.extra_members_hash() + ); + } +}; + +template <> +struct std::hash +{ + auto operator()(const mamba::specs::MatchSpec::ExtraMembers& extra) const -> std::size_t + { + return mamba::util::hash_vals( + extra.filename, + extra.subdirs, + extra.md5, + extra.sha256, + extra.license, + extra.license_family, + extra.features, + extra.track_features, + extra.optional + ); + } +}; + #endif diff --git a/libmamba/include/mamba/specs/package_info.hpp b/libmamba/include/mamba/specs/package_info.hpp index 4623c30baa..cdd8916d55 100644 --- a/libmamba/include/mamba/specs/package_info.hpp +++ b/libmamba/include/mamba/specs/package_info.hpp @@ -15,6 +15,7 @@ #include "mamba/specs/error.hpp" #include "mamba/specs/platform.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -81,4 +82,47 @@ namespace mamba::specs void from_json(const nlohmann::json& j, PackageInfo& pkg); } + +template <> +struct std::hash +{ + auto operator()(const mamba::specs::PackageInfo& pkg) const -> std::size_t + { + auto seed = std::size_t(0); + seed = mamba::util::hash_vals( + seed, + pkg.name, + pkg.version, + pkg.build_string, + pkg.build_number, + pkg.channel, + pkg.package_url, + pkg.platform, + pkg.filename, + pkg.license, + pkg.md5, + pkg.sha256, + pkg.signatures + ); + seed = mamba::util::hash_combine_val_range( + seed, + pkg.track_features.begin(), + pkg.track_features.end() + ); + seed = mamba::util::hash_combine_val_range( + seed, + pkg.dependencies.begin(), + pkg.dependencies.end() + ); + seed = mamba::util::hash_combine_val_range(seed, pkg.constrains.begin(), pkg.constrains.end()); + seed = mamba::util::hash_combine_val_range( + seed, + pkg.defaulted_keys.begin(), + pkg.defaulted_keys.end() + ); + seed = mamba::util::hash_vals(seed, pkg.noarch, pkg.size, pkg.timestamp, pkg.package_type); + return seed; + } +}; + #endif diff --git a/libmamba/include/mamba/specs/regex_spec.hpp b/libmamba/include/mamba/specs/regex_spec.hpp index 28420a0b6c..78610549e1 100644 --- a/libmamba/include/mamba/specs/regex_spec.hpp +++ b/libmamba/include/mamba/specs/regex_spec.hpp @@ -47,6 +47,18 @@ namespace mamba::specs [[nodiscard]] auto str() const -> const std::string&; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const RegexSpec& other) const -> bool + { + return m_raw_pattern == other.m_raw_pattern + && m_pattern.flags() == other.m_pattern.flags(); + } + + [[nodiscard]] auto operator!=(const RegexSpec& other) const -> bool + { + return !(*this == other); + } + private: std::regex m_pattern; diff --git a/libmamba/include/mamba/specs/unresolved_channel.hpp b/libmamba/include/mamba/specs/unresolved_channel.hpp index ac8096be8a..120dcef805 100644 --- a/libmamba/include/mamba/specs/unresolved_channel.hpp +++ b/libmamba/include/mamba/specs/unresolved_channel.hpp @@ -17,6 +17,7 @@ #include "mamba/specs/error.hpp" #include "mamba/specs/platform.hpp" #include "mamba/util/flat_set.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -111,6 +112,18 @@ namespace mamba::specs [[nodiscard]] auto str() const -> std::string; + [[nodiscard]] auto operator==(const UnresolvedChannel& other) const -> bool + { + return m_location == other.m_location // + && m_platform_filters == other.m_platform_filters // + && m_type == other.m_type; + } + + [[nodiscard]] auto operator!=(const UnresolvedChannel& other) const -> bool + { + return !(*this == other); + } + private: std::string m_location = std::string(unknown_channel); @@ -137,4 +150,13 @@ struct fmt::formatter auto format(const UnresolvedChannel& uc, format_context& ctx) const -> format_context::iterator; }; +template <> +struct std::hash +{ + auto operator()(const mamba::specs::UnresolvedChannel& uc) const -> std::size_t + { + return mamba::util::hash_vals(uc.location(), uc.platform_filters(), static_cast(uc.type())); + } +}; + #endif diff --git a/libmamba/include/mamba/specs/version_spec.hpp b/libmamba/include/mamba/specs/version_spec.hpp index 62228e163f..7ed08ae8cd 100644 --- a/libmamba/include/mamba/specs/version_spec.hpp +++ b/libmamba/include/mamba/specs/version_spec.hpp @@ -17,6 +17,7 @@ #include "mamba/specs/error.hpp" #include "mamba/specs/version.hpp" #include "mamba/util/flat_bool_expr_tree.hpp" +#include "mamba/util/tuple_hash.hpp" namespace mamba::specs { @@ -195,6 +196,16 @@ namespace mamba::specs */ [[nodiscard]] auto expression_size() const -> std::size_t; + [[nodiscard]] auto operator==(const VersionSpec& other) const -> bool + { + return m_tree == other.m_tree; + } + + [[nodiscard]] auto operator!=(const VersionSpec& other) const -> bool + { + return !(*this == other); + } + private: tree_type m_tree; @@ -234,4 +245,23 @@ struct fmt::formatter auto format(const ::mamba::specs::VersionSpec& spec, format_context& ctx) -> decltype(ctx.out()); }; + +template <> +struct std::hash +{ + auto operator()(const mamba::specs::VersionPredicate& pred) const -> std::size_t + { + return mamba::util::hash_vals(pred.str()); + } +}; + +template <> +struct std::hash +{ + auto operator()(const mamba::specs::VersionSpec& spec) const -> std::size_t + { + return mamba::util::hash_vals(spec.str()); + } +}; + #endif diff --git a/libmamba/include/mamba/util/flat_binary_tree.hpp b/libmamba/include/mamba/util/flat_binary_tree.hpp index 749ba9ad16..01c3b26813 100644 --- a/libmamba/include/mamba/util/flat_binary_tree.hpp +++ b/libmamba/include/mamba/util/flat_binary_tree.hpp @@ -38,6 +38,19 @@ namespace mamba::util branch_type data; std::size_t left_child = 0; std::size_t right_child = 0; + + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const branch_node& other) const -> bool + { + return data == other.data // + && left_child == other.left_child // + && right_child == other.right_child; + } + + [[nodiscard]] auto operator!=(const branch_node& other) const -> bool + { + return !(*this == other); + } }; using leaf_node = leaf_type; @@ -90,6 +103,17 @@ namespace mamba::util [[nodiscard]] auto right(idx_type idx) const -> idx_type; [[nodiscard]] auto root() const -> idx_type; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const flat_binary_tree& other) const -> bool + { + return m_nodes == other.m_nodes && m_root == other.m_root; + } + + [[nodiscard]] auto operator!=(const flat_binary_tree& other) const -> bool + { + return !(*this == other); + } + template void dfs_raw(Visitor&& visitor, idx_type start) const; diff --git a/libmamba/include/mamba/util/flat_bool_expr_tree.hpp b/libmamba/include/mamba/util/flat_bool_expr_tree.hpp index 93f797b0fc..abab3921ae 100644 --- a/libmamba/include/mamba/util/flat_bool_expr_tree.hpp +++ b/libmamba/include/mamba/util/flat_bool_expr_tree.hpp @@ -173,6 +173,17 @@ namespace mamba::util template void infix_for_each(UnaryFunc&& func) const; + // TODO(C++20): replace by the `= default` implementation of `operator==` + [[nodiscard]] auto operator==(const self_type& other) const -> bool + { + return m_tree == other.m_tree; + } + + [[nodiscard]] auto operator!=(const self_type& other) const -> bool + { + return !(*this == other); + } + private: using idx_type = typename tree_type::idx_type; diff --git a/libmamba/include/mamba/util/flat_set.hpp b/libmamba/include/mamba/util/flat_set.hpp index 55c7a96256..8796edd902 100644 --- a/libmamba/include/mamba/util/flat_set.hpp +++ b/libmamba/include/mamba/util/flat_set.hpp @@ -12,6 +12,8 @@ #include #include +#include "mamba/util/tuple_hash.hpp" + namespace mamba::util { @@ -557,4 +559,14 @@ namespace mamba::util return out; } } + +template +struct std::hash> +{ + auto operator()(const mamba::util::flat_set& set) const -> std::size_t + { + return mamba::util::hash_vals(set); + } +}; + #endif diff --git a/libmamba/include/mamba/util/heap_optional.hpp b/libmamba/include/mamba/util/heap_optional.hpp index 123bae5c67..2ff7e06e91 100644 --- a/libmamba/include/mamba/util/heap_optional.hpp +++ b/libmamba/include/mamba/util/heap_optional.hpp @@ -68,6 +68,20 @@ namespace mamba::util void reset(); + [[nodiscard]] auto operator==(const heap_optional& other) const -> bool + { + if (has_value() && other.has_value()) + { + return *m_ptr == *other; + } + return !has_value() && !other.has_value(); + } + + [[nodiscard]] auto operator!=(const heap_optional& other) const -> bool + { + return !(*this == other); + } + private: std::unique_ptr m_ptr = nullptr; @@ -236,4 +250,18 @@ namespace mamba::util m_ptr = nullptr; } } + +template +struct std::hash> +{ + std::size_t operator()(const mamba::util::heap_optional& opt) const + { + if (opt.has_value()) + { + return std::hash{}(*opt); + } + return 0; + } +}; + #endif