Remove usage of `std::hash<google::protobuf::MapKey` and use a single `absl::HashOf` call when calculating the bucket

PiperOrigin-RevId: 679573919
pull/18525/head
Protobuf Team Bot 5 months ago committed by Copybara-Service
parent 3bf9c400ec
commit 6fe67db7c0
  1. 4
      src/google/protobuf/map.cc
  2. 51
      src/google/protobuf/map.h
  3. 21
      src/google/protobuf/map_field.h
  4. 7
      src/google/protobuf/map_test.inc

@ -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)) {

@ -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<std::is_scalar<key_type>::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<absl::string_view> as it supports the input types we care about.
// absl::Hash<absl::string_view> 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<uint64_t>{}(integral)
: absl::Hash<absl::string_view>{}(
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 <typename T, typename = void>
struct RealKeyToVariantKeyAlternative;
template <typename T>
struct RealKeyToVariantKeyAlternative<
T, typename std::enable_if<std::is_integral<T>::value>::type> {
uint64_t operator()(uint64_t value) const { return value; }
};
template <>
struct RealKeyToVariantKey<std::string> {
template <typename T>
@ -386,6 +389,11 @@ struct RealKeyToVariantKey<std::string> {
}
};
template <>
struct RealKeyToVariantKeyAlternative<std::string> {
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<VariantKey, NodeBase*, std::less<VariantKey>,
@ -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<size_t>(key.integral)));
}
map_index_t VariantBucketNumber(absl::string_view key) const {
return static_cast<map_index_t>(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<map_index_t>(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<Key>{}(k)));
return BucketNumberFromHash(hash_function()(k));
ABSL_DCHECK_EQ(
VariantBucketNumber(RealKeyToVariantKeyAlternative<Key>{}(k)),
VariantBucketNumber(RealKeyToVariantKey<Key>{}(k)));
return VariantBucketNumber(RealKeyToVariantKeyAlternative<Key>{}(k));
}
// Assumes node_ and m_ are correct and non-null, but other fields may be

@ -273,26 +273,15 @@ struct RealKeyToVariantKey<MapKey> {
VariantKey operator()(const MapKey& value) const;
};
} // namespace internal
} // namespace protobuf
} // namespace google
namespace std {
template <>
struct hash<google::protobuf::MapKey> {
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<MapKey> {
VariantKey operator()(const MapKey& value) const {
return RealKeyToVariantKey<MapKey>{}(value);
}
};
} // namespace std
namespace google {
namespace protobuf {
} // namespace internal
namespace internal {
class ContendedMapCleanTest;

@ -336,6 +336,13 @@ struct RealKeyToVariantKey<MoveTestKey> {
}
};
template <>
struct RealKeyToVariantKeyAlternative<MoveTestKey> {
VariantKey operator()(const MoveTestKey& value) const {
return VariantKey(value.data);
}
};
namespace {
TEST_F(MapImplTest, OperatorBracketRValue) {

Loading…
Cancel
Save