// Copyright 2025 Huawei Cloud Computing Technology Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef INCLUDED_SRC_UTILS_CPP_BACK_MAP_HPP #define INCLUDED_SRC_UTILS_CPP_BACK_MAP_HPP #include #include #include #include #include #include #include #include // IWYU pragma: keep #include "gsl/gsl" /// \brief Backmap a container of Values to Keys using a given Converter, and /// provide an interface for quick lookup of values by keys. /// \tparam TKey Result of conversion. Stored by value, std::hash /// MUST be available. /// \tparam TValue Source data for conversion. Stored by pointer, the /// source container MUST stay alive. template class BackMap final { public: template using IsKeyWithError = std::bool_constant and std::is_convertible_v>; /// \brief Converter from TValue to TKey. The return type may be different /// from TKey, in such a case Converter is considered a function that DOES /// return TKey, but may fail and return std::nullopt or an unexpected. /// TResult must be either the same type as TKey, or support conversions /// from TKey and to bool. template requires(std::is_same_v or IsKeyWithError::value) using Converter = std::function; /// \brief Create a BackMap by iterating over container and applying /// Converter. /// \param container Container to iterate over. begin() and end() methods /// must be available. /// \param converter Converter to apply to elements of the container. /// \return BackMap on success or std::nullopt on failure. In particular, if /// any exception is thrown from internal containers or converter returns an /// error, std::nullopt is returned. template [[nodiscard]] static auto Make(TContainer const* const container, Converter const& converter) noexcept -> std::optional { if (container == nullptr or converter == nullptr) { return std::nullopt; } auto const hasher = std::hash{}; auto const size = std::distance(container->begin(), container->end()); std::unordered_set keys; std::unordered_map> mapping; try { keys.reserve(size); mapping.reserve(size); for (auto const& value : *container) { std::optional key = BackMap::Convert(converter, value); if (not key.has_value()) { return std::nullopt; } std::size_t const hash = std::invoke(hasher, *key); if (not mapping.contains(hash)) { keys.emplace(*std::move(key)); mapping.insert_or_assign(hash, &value); } } } catch (...) { return std::nullopt; } return BackMap(std::move(keys), std::move(mapping)); } template requires(std::is_invocable_v) [[nodiscard]] static auto Make(TContainer const* const container, TConverter const& converter) noexcept -> std::optional { using TResult = std::invoke_result_t; return Make(container, converter); } /// \brief Obtain all available keys. [[nodiscard]] auto GetKeys() const noexcept -> std::unordered_set const& { return keys_; } /// \brief Obtain a pointer to the value corresponding to the given key. /// Copy free. /// \return If the key is known, a pointer to the value is returned, /// otherwise std::nullopt. [[nodiscard]] auto GetReference(TKey const& key) const noexcept -> std::optional> { auto const hash = std::invoke(std::hash{}, key); if (auto it = mapping_.find(hash); it != mapping_.end()) { return it->second; } return std::nullopt; } /// \brief Obtain the set of values corresponding to given keys. If a key /// isn't known to the container, it is ignored. Perform deep copy of /// referenced values. [[nodiscard]] auto GetValues(std::unordered_set const& keys) const noexcept -> std::unordered_set { std::unordered_set result; result.reserve(keys.size()); for (auto const& key : keys) { if (auto value = GetReference(key)) { result.emplace(*value.value()); } } return result; } using Iterable = std::unordered_map, gsl::not_null>; /// \brief Obtain an iterable key-value set where values correspond to the /// given keys. If a key isn't known to the container, it is ignored. Copy /// free. [[nodiscard]] auto IterateReferences( std::unordered_set const* keys) const noexcept -> Iterable { Iterable result; result.reserve(keys->size()); for (auto const& key : *keys) { if (auto value = GetReference(key)) { result.insert_or_assign(&key, value.value()); } } return result; } private: std::unordered_set keys_; std::unordered_map> mapping_; explicit BackMap( std::unordered_set keys, std::unordered_map> mapping) noexcept : keys_{std::move(keys)}, mapping_{std::move(mapping)} {} template [[nodiscard]] static auto Convert(Converter const& converter, TValue const& value) noexcept -> std::optional { if constexpr (not std::is_same_v) { TResult converted = std::invoke(converter, value); if (not static_cast(converted)) { return std::nullopt; } return *std::move(converted); } else { return std::invoke(converter, value); } } }; #endif // INCLUDED_SRC_UTILS_CPP_BACK_MAP_HPP