15 #include "absl/hash/hash.h"
21 #include <forward_list>
33 #include <type_traits>
34 #include <unordered_map>
38 #include "gmock/gmock.h"
39 #include "gtest/gtest.h"
40 #include "absl/container/flat_hash_set.h"
41 #include "absl/hash/hash_testing.h"
42 #include "absl/hash/internal/spy_hash_state.h"
43 #include "absl/meta/type_traits.h"
44 #include "absl/numeric/int128.h"
45 #include "absl/strings/cord_test_helpers.h"
65 using is_hashable = std::is_default_constructible<absl::Hash<T>>;
81 absl::Hash<std::tuple<TypeParam>>{}(std::tuple<TypeParam>(
n)));
89 enum LegacyEnum { kValue1, kValue2, kValue3 };
91 enum class EnumClass { kValue4, kValue5, kValue6 };
93 TEST(HashValueTest, EnumAndBool) {
99 LegacyEnum::kValue1, LegacyEnum::kValue2, LegacyEnum::kValue3)));
101 EnumClass::kValue4, EnumClass::kValue5, EnumClass::kValue6)));
106 TEST(HashValueTest, FloatingPoint) {
113 -std::numeric_limits<float>::infinity())));
117 -std::numeric_limits<double>::infinity())));
122 .5
L, 1.
L, 2.
L, 4.
L, 42.
L, 0.
L, -0.
L,
124 std::numeric_limits<long double>::infinity(),
125 -std::numeric_limits<long double>::infinity())));
139 TEST(HashValueTest, PointerAlignment) {
143 constexpr
size_t kTotalSize = 1 << 20;
144 std::unique_ptr<char[]>
data(
new char[kTotalSize]);
145 constexpr
size_t kLog2NumValues = 5;
146 constexpr
size_t kNumValues = 1 << kLog2NumValues;
148 for (
size_t align = 1;
align < kTotalSize / kNumValues;
154 size_t bits_and = ~size_t{};
156 for (
size_t i = 0;
i < kNumValues; ++
i) {
163 constexpr
size_t kMask = (1 << (kLog2NumValues + 7)) - 1;
164 size_t stuck_bits = (~bits_or | bits_and) & kMask;
165 EXPECT_EQ(stuck_bits, 0) <<
"0x" << std::hex << stuck_bits;
169 TEST(HashValueTest, PairAndTuple) {
176 std::make_pair(0, 42), std::make_pair(0, 42), std::make_pair(42, 0),
177 std::make_pair(0, 0), std::make_pair(42, 42), std::make_pair(1, 42))));
186 int a = 0,
b = 1,
c = 17,
d = 23;
188 std::tie(
a,
a), std::tie(
a,
b), std::tie(
b,
c), std::tie(
c,
d))));
192 std::forward_as_tuple(0, 0, 0), std::forward_as_tuple(0, 0, 42),
193 std::forward_as_tuple(0, 23, 0), std::forward_as_tuple(17, 0, 0),
194 std::forward_as_tuple(42, 0, 0), std::forward_as_tuple(3, 9, 9),
195 std::forward_as_tuple(0, 0, -42))));
198 TEST(HashValueTest, CombineContiguousWorks) {
202 auto vh1 = SpyHash(v1);
203 auto vh2 = SpyHash(v2);
207 struct DummyDeleter {
208 template <
typename T>
209 void operator() (
T*
ptr) {}
212 struct SmartPointerEq {
213 template <
typename T,
typename U>
214 bool operator()(
const T& t,
const U&
u)
const {
215 return GetPtr(t) == GetPtr(
u);
218 template <
typename T>
219 static auto GetPtr(
const T& t) -> decltype(&*t) {
220 return t ? &*
t :
nullptr;
223 static std::nullptr_t GetPtr(std::nullptr_t) {
return nullptr; }
226 TEST(HashValueTest, SmartPointers) {
232 std::unique_ptr<int, DummyDeleter> unique1(&
i);
233 std::unique_ptr<int, DummyDeleter> unique2(&
i);
234 std::unique_ptr<int, DummyDeleter> unique_other(&j);
235 std::unique_ptr<int, DummyDeleter> unique_null;
237 std::shared_ptr<int> shared1(&
i, DummyDeleter());
238 std::shared_ptr<int> shared2(&
i, DummyDeleter());
239 std::shared_ptr<int> shared_other(&j, DummyDeleter());
240 std::shared_ptr<int> shared_null;
245 ASSERT_TRUE(SmartPointerEq{}(unique_null,
nullptr));
249 std::forward_as_tuple(&
i,
nullptr,
250 unique1, unique2, unique_null,
251 absl::make_unique<int>(),
252 shared1, shared2, shared_null,
253 std::make_shared<int>()),
257 TEST(HashValueTest, FunctionPointer) {
261 Func p1 = [] {
return 2; }, p2 = [] {
return 1; };
267 template <
typename T>
268 std::tuple<int, T, size_t> operator()(
const T& t)
const {
283 size_t halfway = sv.
size() / 2;
284 std::vector<absl::string_view> parts = {sv.
substr(0, halfway),
307 const WrapInTuple
t{};
320 EXPECT_NE(SpyHash(
static_cast<const char*
>(
"ABC")),
324 TEST(HashValueTest, WString) {
333 TEST(HashValueTest, U16String) {
337 std::u16string(), std::u16string(
u"ABC"), std::u16string(
u"ABC"),
338 std::u16string(
u"Some other different string"),
339 std::u16string(
u"Iñtërnâtiônà lizætiøn"))));
342 TEST(HashValueTest, U32String) {
346 std::u32string(), std::u32string(U
"ABC"), std::u32string(U
"ABC"),
347 std::u32string(U
"Some other different string"),
348 std::u32string(U
"Iñtërnâtiônà lizætiøn"))));
351 TEST(HashValueTest, StdArray) {
355 std::make_tuple(std::array<int, 3>{}, std::array<int, 3>{{0, 23, 42}})));
358 TEST(HashValueTest, StdBitset) {
362 {std::bitset<2>(
"00"), std::bitset<2>(
"01"), std::bitset<2>(
"10"),
363 std::bitset<2>(
"11")}));
365 {std::bitset<5>(
"10101"), std::bitset<5>(
"10001"), std::bitset<5>()}));
367 constexpr
int kNumBits = 256;
368 std::array<std::string, 6> bit_strings;
370 bit_strings[1][0] =
'0';
371 bit_strings[2][1] =
'0';
372 bit_strings[3][kNumBits / 3] =
'0';
373 bit_strings[4][kNumBits - 2] =
'0';
374 bit_strings[5][kNumBits - 1] =
'0';
376 {std::bitset<kNumBits>(bit_strings[0].
c_str()),
377 std::bitset<kNumBits>(bit_strings[1].
c_str()),
378 std::bitset<kNumBits>(bit_strings[2].
c_str()),
379 std::bitset<kNumBits>(bit_strings[3].
c_str()),
380 std::bitset<kNumBits>(bit_strings[4].
c_str()),
381 std::bitset<kNumBits>(bit_strings[5].
c_str())}));
384 template <
typename T>
399 TypeParam{
a,
b}, TypeParam{
b,
c})));
403 using IntSequenceTypes =
405 std::vector<int>, std::vector<bool>, std::set<int>,
415 template <
typename H>
417 return H::combine(
std::move(h), std::abs(
p.i));
421 return std::abs(
a.i) == std::abs(
b.i);
424 friend std::ostream&
operator<<(std::ostream&
o, Private
p) {
432 class PiecewiseHashTester {
443 split_locations_(
std::
move(split_locations)) {}
445 template <
typename H>
448 return H::combine_contiguous(
std::move(h),
p.buf_.data(),
p.buf_.size());
451 if (
p.split_locations_.empty()) {
456 for (
size_t next :
p.split_locations_) {
462 if (!last_chunk.
empty()) {
472 std::set<size_t> split_locations_;
478 template <
typename H>
480 const char*
foo =
"foo";
481 const char*
bar =
"bar";
488 TEST(HashValueTest, CombinePiecewiseBuffer) {
496 hash(PiecewiseHashTester(
"foobar", {})));
498 hash(PiecewiseHashTester(
"foobar", {3})));
511 for (
size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) {
514 for (
int i = 0;
i < big_buffer_size; ++
i) {
516 big_buffer.push_back(32 + (
i * (
i / 3)) % 64);
518 auto big_buffer_hash =
hash(PiecewiseHashTester(big_buffer));
520 const int possible_breaks = 9;
521 size_t breaks[possible_breaks] = {1, 512, 1023, 1024, 1025,
522 1536, 2047, 2048, 2049};
523 for (
unsigned test_mask = 0; test_mask < (1
u << possible_breaks);
526 std::set<size_t> break_locations;
527 for (
int j = 0;
j < possible_breaks; ++
j) {
528 if (test_mask & (1
u << j)) {
529 break_locations.insert(breaks[j]);
533 hash(PiecewiseHashTester(big_buffer,
std::move(break_locations))),
539 TEST(HashValueTest, PrivateSanity) {
542 EXPECT_NE(SpyHash(Private{0}), SpyHash(Private{1}));
543 EXPECT_EQ(SpyHash(Private{1}), SpyHash(Private{1}));
554 TEST(HashValueTest, Variant) {
559 V(Private{1}), V(Private{-1}), V(Private{2}), V(
"ABC"), V(
"BCD"))));
561 #if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
567 TEST(HashValueTest, Maps) {
570 using M = std::map<int, std::string>;
572 M{}, M{{0,
"foo"}}, M{{1,
"foo"}}, M{{0,
"bar"}}, M{{1,
"bar"}},
573 M{{0,
"foo"}, {42,
"bar"}}, M{{1,
"foo"}, {42,
"bar"}},
574 M{{1,
"foo"}, {43,
"bar"}}, M{{1,
"foo"}, {43,
"baz"}})));
576 using MM = std::multimap<int, std::string>;
578 MM{}, MM{{0,
"foo"}}, MM{{1,
"foo"}}, MM{{0,
"bar"}}, MM{{1,
"bar"}},
579 MM{{0,
"foo"}, {0,
"bar"}}, MM{{0,
"bar"}, {0,
"foo"}},
580 MM{{0,
"foo"}, {42,
"bar"}}, MM{{1,
"foo"}, {42,
"bar"}},
581 MM{{1,
"foo"}, {1,
"foo"}, {43,
"bar"}}, MM{{1,
"foo"}, {43,
"baz"}})));
584 TEST(HashValueTest, ReferenceWrapper) {
587 Private p1{1}, p10{10};
592 int one = 1, ten = 10;
598 std::tuple<std::reference_wrapper<int>>(
std::ref(ten)),
599 std::tuple<int>(one), std::tuple<int>(ten))));
602 template <
typename T,
typename =
void>
605 template <
typename T>
606 struct IsHashCallable<
T,
absl::
void_t<decltype(std::declval<absl::Hash<T>>()(
609 template <
typename T,
typename =
void>
612 template <
typename T>
613 struct IsAggregateInitializable<
T,
absl::
void_t<decltype(T{})>>
616 TEST(IsHashableTest, ValidHash) {
627 #if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
628 TEST(IsHashableTest, PoisonHash) {
637 #if !defined(__GNUC__) || __GNUC__ < 9
642 #endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
649 template <
typename HashCode>
655 struct EmptyCombine {
656 template <
typename HashCode>
662 template <
typename Int>
663 struct CombineIterative {
664 template <
typename HashCode>
666 for (
int i = 0;
i < 5; ++
i) {
673 template <
typename Int>
674 struct CombineVariadic {
675 template <
typename HashCode>
681 enum class InvokeTag {
682 kUniquelyRepresented,
684 #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
686 #endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
691 template <InvokeTag T>
692 using InvokeTagConstant = std::integral_constant<InvokeTag, T>;
694 template <InvokeTag... Tags>
697 template <InvokeTag
a, InvokeTag
b, InvokeTag... Tags>
698 struct MinTag<
a,
b, Tags...> : MinTag<(a < b ? a : b), Tags...> {};
700 template <InvokeTag a>
701 struct MinTag<a> : InvokeTagConstant<a> {};
703 template <InvokeTag... Tags>
704 struct CustomHashType {
705 explicit CustomHashType(size_t val) : value(val) {}
709 template <InvokeTag allowed, InvokeTag... tags>
710 struct EnableIfContained
711 : std::enable_if<absl::disjunction<
712 std::integral_constant<bool, allowed == tags>...>::value> {};
715 typename H, InvokeTag... Tags,
716 typename = typename EnableIfContained<InvokeTag::kHashValue, Tags...>::type>
717 H AbslHashValue(H state, CustomHashType<Tags...> t) {
718 static_assert(MinTag<Tags...>::value == InvokeTag::kHashValue, "");
719 return H::combine(std::move(state),
720 t.value + static_cast<int>(InvokeTag::kHashValue));
727 namespace hash_internal {
728 template <InvokeTag... Tags>
729 struct is_uniquely_represented<
730 CustomHashType<Tags...>,
731 typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type>
737 #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
738 namespace ABSL_INTERNAL_LEGACY_HASH_NAMESPACE {
739 template <InvokeTag... Tags>
740 struct hash<CustomHashType<Tags...>> {
741 template <InvokeTag... TagsIn, typename = typename EnableIfContained<
742 InvokeTag::kLegacyHash, TagsIn...>::type>
743 size_t operator()(CustomHashType<TagsIn...> t) const {
744 static_assert(MinTag<Tags...>::value == InvokeTag::kLegacyHash, "");
745 return t.value + static_cast<int>(InvokeTag::kLegacyHash);
752 template <InvokeTag... Tags>
753 struct hash<CustomHashType<Tags...>> {
754 template <InvokeTag... TagsIn, typename = typename EnableIfContained<
755 InvokeTag::kStdHash, TagsIn...>::type>
756 size_t operator()(CustomHashType<TagsIn...> t) const {
757 static_assert(MinTag<Tags...>::value == InvokeTag::kStdHash, "");
758 return t.value + static_cast<int>(InvokeTag::kStdHash);
765 template <typename... T>
766 void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>, T...) {
767 using type = CustomHashType<T::value...>;
768 SCOPED_TRACE(testing::PrintToString(std::vector<InvokeTag>{T::value...}));
769 EXPECT_TRUE(is_hashable<type>());
770 EXPECT_TRUE(is_hashable<const type>());
771 EXPECT_TRUE(is_hashable<const type&>());
773 const size_t offset = static_cast<int>(std::min({T::value...}));
774 EXPECT_EQ(SpyHash(type(7)), SpyHash(size_t{7 + offset}));
777 void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>) {
778 #if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
780 using type = CustomHashType<>;
781 EXPECT_FALSE(is_hashable<type>());
782 EXPECT_FALSE(is_hashable<const type>());
783 EXPECT_FALSE(is_hashable<const type&>());
787 template <InvokeTag Tag, typename... T>
788 void TestCustomHashType(InvokeTagConstant<Tag> tag, T... t) {
789 constexpr auto next = static_cast<InvokeTag>(static_cast<int>(Tag) + 1);
790 TestCustomHashType(InvokeTagConstant<next>(), tag, t...);
791 TestCustomHashType(InvokeTagConstant<next>(), t...);
794 TEST(HashTest, CustomHashType) {
795 TestCustomHashType(InvokeTagConstant<InvokeTag{}>());
798 TEST(HashTest, NoOpsAreEquivalent) {
799 EXPECT_EQ(Hash<NoOp>()({}), Hash<NoOp>()({}));
800 EXPECT_EQ(Hash<NoOp>()({}), Hash<EmptyCombine>()({}));
803 template <typename T>
804 class HashIntTest : public testing::Test {
806 TYPED_TEST_SUITE_P(HashIntTest);
808 TYPED_TEST_P(HashIntTest, BasicUsage) {
809 EXPECT_NE(Hash<NoOp>()({}), Hash<TypeParam>()(0));
810 EXPECT_NE(Hash<NoOp>()({}),
811 Hash<TypeParam>()(std::numeric_limits<TypeParam>::max()));
812 if (std::numeric_limits<TypeParam>::min() != 0) {
813 EXPECT_NE(Hash<NoOp>()({}),
814 Hash<TypeParam>()(std::numeric_limits<TypeParam>::min()));
817 EXPECT_EQ(Hash<CombineIterative<TypeParam>>()({}),
818 Hash<CombineVariadic<TypeParam>>()({}));
821 REGISTER_TYPED_TEST_CASE_P(HashIntTest, BasicUsage);
822 using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t,
823 uint32_t, uint64_t, size_t>;
824 INSTANTIATE_TYPED_TEST_CASE_P(My, HashIntTest, IntTypes);
826 struct StructWithPadding {
830 template <typename H>
831 friend H AbslHashValue(H hash_state, const StructWithPadding& s) {
832 return H::combine(std::move(hash_state), s.c, s.i);
836 static_assert(sizeof(StructWithPadding) > sizeof(char) + sizeof(int),
837 "StructWithPadding doesn't have padding");
838 static_assert(std::is_standard_layout<StructWithPadding>::value, "");
848 template <
typename H>
850 for (
auto t =
slice.begin; t !=
slice.end; ++t) {
851 hash_state = H::combine(
std::move(hash_state), *t);
857 TEST(HashTest, HashNonUniquelyRepresentedType) {
860 static const size_t kNumStructs = 10;
861 unsigned char buffer1[kNumStructs *
sizeof(StructWithPadding)];
863 auto* s1 =
reinterpret_cast<StructWithPadding*
>(buffer1);
865 unsigned char buffer2[kNumStructs *
sizeof(StructWithPadding)];
867 auto* s2 =
reinterpret_cast<StructWithPadding*
>(buffer2);
868 for (
int i = 0;
i < kNumStructs; ++
i) {
870 s1[
i].c = s2[
i].c =
'0' +
i;
871 s1[
i].i = s2[
i].i =
i;
873 buffer2 +
i *
sizeof(StructWithPadding),
874 sizeof(StructWithPadding)) == 0)
875 <<
"Bug in test code: objects do not have unequal"
876 <<
" object representations";
880 EXPECT_EQ(
Hash<ArraySlice<StructWithPadding>>()({s1, s1 + kNumStructs}),
881 Hash<ArraySlice<StructWithPadding>>()({s2, s2 + kNumStructs}));
884 TEST(HashTest, StandardHashContainerUsage) {
885 std::unordered_map<int, std::string, Hash<int>>
map = {{0,
"foo"},
893 struct ConvertibleFromNoOp {
894 ConvertibleFromNoOp(
NoOp) {}
896 template <
typename H>
898 return H::combine(
std::move(hash_state), 1);
902 TEST(HashTest, HeterogeneousCall) {
907 TEST(IsUniquelyRepresentedTest, SanityTest) {
916 struct IntAndString {
920 template <
typename H>
922 return H::combine(
std::move(hash_state), int_and_string.s,
927 TEST(HashTest, SmallValueOn64ByteBoundary) {
934 template <
typename H>
945 TEST(HashTest, TypeErased) {
949 EXPECT_EQ(SpyHash(TypeErased{7}), SpyHash(
size_t{7}));
950 EXPECT_NE(SpyHash(TypeErased{7}), SpyHash(
size_t{13}));
952 EXPECT_EQ(SpyHash(std::make_pair(TypeErased{7}, 17)),
953 SpyHash(std::make_pair(
size_t{7}, 17)));
956 struct ValueWithBoolConversion {
957 operator bool()
const {
return false; }
964 struct hash<ValueWithBoolConversion> {
971 TEST(HashTest, DoesNotUseImplicitConversionsToBool) {