diff --git a/src/google/protobuf/map.cc b/src/google/protobuf/map.cc index db6784d8c9..570b61bec8 100644 --- a/src/google/protobuf/map.cc +++ b/src/google/protobuf/map.cc @@ -50,10 +50,6 @@ void UntypedMapBase::EraseFromTree(map_index_t b, } } -map_index_t UntypedMapBase::VariantBucketNumber(VariantKey key) const { - return BucketNumberFromHash(key.Hash()); -} - void UntypedMapBase::InsertUniqueInTree(map_index_t b, GetKey get_key, NodeBase* node) { if (TableEntryIsNonEmptyList(b)) { diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index a65f8f2d63..7b28ffd5aa 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -213,7 +213,7 @@ struct TransparentSupport { // We hash all the scalars as uint64_t so that we can implement the same hash // function for VariantKey. This way we can have MapKey provide the same hash // as the underlying value would have. - using hash = std::hash< + using hash = absl::Hash< std::conditional_t::value, uint64_t, key_type>>; static bool Equals(const key_type& a, const key_type& b) { return a == b; } @@ -227,7 +227,7 @@ struct TransparentSupport { }; // We add transparent support for std::string keys. We use -// std::hash as it supports the input types we care about. +// absl::Hash as it supports the input types we care about. // The lookup functions accept arbitrary `K`. This will include any key type // that is convertible to absl::string_view. template <> @@ -350,12 +350,6 @@ struct VariantKey { if (data == nullptr) data = ""; } - size_t Hash() const { - return data == nullptr ? std::hash{}(integral) - : absl::Hash{}( - absl::string_view(data, integral)); - } - friend bool operator<(const VariantKey& left, const VariantKey& right) { ABSL_DCHECK_EQ(left.data == nullptr, right.data == nullptr); if (left.integral != right.integral) { @@ -378,6 +372,15 @@ struct RealKeyToVariantKey { VariantKey operator()(T value) const { return VariantKey(value); } }; +template +struct RealKeyToVariantKeyAlternative; + +template +struct RealKeyToVariantKeyAlternative< + T, typename std::enable_if::value>::type> { + uint64_t operator()(uint64_t value) const { return value; } +}; + template <> struct RealKeyToVariantKey { template @@ -386,6 +389,11 @@ struct RealKeyToVariantKey { } }; +template <> +struct RealKeyToVariantKeyAlternative { + absl::string_view operator()(absl::string_view value) const { return value; } +}; + // We use a single kind of tree for all maps. This reduces code duplication. using TreeForMap = absl::btree_map, @@ -689,13 +697,21 @@ class PROTOBUF_EXPORT UntypedMapBase { TableEntryPtr ConvertToTree(NodeBase* node, GetKey get_key); void EraseFromTree(map_index_t b, typename Tree::iterator tree_it); - map_index_t VariantBucketNumber(VariantKey key) const; + map_index_t VariantBucketNumber(VariantKey key) const { + return key.data == nullptr + ? VariantBucketNumber(key.integral) + : VariantBucketNumber(absl::string_view( + key.data, static_cast(key.integral))); + } + + map_index_t VariantBucketNumber(absl::string_view key) const { + return static_cast(absl::HashOf(seed_, key) & + (num_buckets_ - 1)); + } - map_index_t BucketNumberFromHash(uint64_t h) const { - // We xor the hash value against the random seed so that we effectively - // have a random hash function. - // We use absl::Hash to do bit mixing for uniform bucket selection. - return absl::HashOf(h ^ seed_) & (num_buckets_ - 1); + map_index_t VariantBucketNumber(uint64_t key) const { + return static_cast(absl::HashOf(key ^ seed_) & + (num_buckets_ - 1)); } TableEntryPtr* CreateEmptyTable(map_index_t n) { @@ -1106,9 +1122,10 @@ class KeyMapBase : public UntypedMapBase { } map_index_t BucketNumber(typename TS::ViewType k) const { - ABSL_DCHECK_EQ(BucketNumberFromHash(hash_function()(k)), - VariantBucketNumber(RealKeyToVariantKey{}(k))); - return BucketNumberFromHash(hash_function()(k)); + ABSL_DCHECK_EQ( + VariantBucketNumber(RealKeyToVariantKeyAlternative{}(k)), + VariantBucketNumber(RealKeyToVariantKey{}(k))); + return VariantBucketNumber(RealKeyToVariantKeyAlternative{}(k)); } // Assumes node_ and m_ are correct and non-null, but other fields may be diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 40d6ad0062..0e37a16d7b 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -273,26 +273,15 @@ struct RealKeyToVariantKey { VariantKey operator()(const MapKey& value) const; }; -} // namespace internal - -} // namespace protobuf -} // namespace google -namespace std { template <> -struct hash { - size_t operator()(const google::protobuf::MapKey& map_key) const { - return ::google::protobuf::internal::RealKeyToVariantKey<::google::protobuf::MapKey>{}(map_key) - .Hash(); - } - bool operator()(const google::protobuf::MapKey& map_key1, - const google::protobuf::MapKey& map_key2) const { - return map_key1 < map_key2; +struct RealKeyToVariantKeyAlternative { + VariantKey operator()(const MapKey& value) const { + return RealKeyToVariantKey{}(value); } }; -} // namespace std -namespace google { -namespace protobuf { +} // namespace internal + namespace internal { class ContendedMapCleanTest; diff --git a/src/google/protobuf/map_test.inc b/src/google/protobuf/map_test.inc index 41548f28fe..6e4d261c13 100644 --- a/src/google/protobuf/map_test.inc +++ b/src/google/protobuf/map_test.inc @@ -336,6 +336,13 @@ struct RealKeyToVariantKey { } }; +template <> +struct RealKeyToVariantKeyAlternative { + VariantKey operator()(const MoveTestKey& value) const { + return VariantKey(value.data); + } +}; + namespace { TEST_F(MapImplTest, OperatorBracketRValue) {