//===- llvm/unittest/Support/HashBuilderTest.cpp - HashBuilder unit tests -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/HashBuilder.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/MD5.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/SHA256.h" #include "gtest/gtest.h" #include #include #include #include #include // gtest utilities and macros rely on using a single type. So wrap both the // hasher type and endianness. template struct HasherTAndEndianness { using HasherT = _HasherT; static constexpr llvm::endianness Endianness = _Endianness; }; using HasherTAndEndiannessToTest = ::testing::Types< HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness, HasherTAndEndianness>; template class HashBuilderTest : public testing::Test {}; TYPED_TEST_SUITE(HashBuilderTest, HasherTAndEndiannessToTest, ); template using HashBuilder = llvm::HashBuilder; template static typename HashBuilder::template HashResultTy<> hashWithBuilder(const Ts &...Args) { return HashBuilder().add(Args...).final(); } template static typename HashBuilder::template HashResultTy<> hashRangeWithBuilder(const Ts &...Args) { return HashBuilder().addRange(Args...).final(); } // All the test infrastructure relies on the variadic helpers. Test them first. TYPED_TEST(HashBuilderTest, VariadicHelpers) { { HashBuilder HBuilder; HBuilder.add(100); HBuilder.add('c'); HBuilder.add("string"); EXPECT_EQ(HBuilder.final(), hashWithBuilder(100, 'c', "string")); } { HashBuilder HBuilder; std::vector Vec{100, 101, 102}; HBuilder.addRange(Vec); EXPECT_EQ(HBuilder.final(), hashRangeWithBuilder(Vec)); } { HashBuilder HBuilder; std::vector Vec{200, 201, 202}; HBuilder.addRange(Vec.begin(), Vec.end()); EXPECT_EQ(HBuilder.final(), hashRangeWithBuilder(Vec.begin(), Vec.end())); } } TYPED_TEST(HashBuilderTest, AddRangeElements) { HashBuilder HBuilder; int Values[] = {1, 2, 3}; HBuilder.addRangeElements(llvm::ArrayRef(Values)); EXPECT_EQ(HBuilder.final(), hashWithBuilder(1, 2, 3)); } TYPED_TEST(HashBuilderTest, AddHashableData) { using HE = TypeParam; auto ByteSwapAndHashWithHasher = [](auto Data) { using H = typename HE::HasherT; constexpr auto E = HE::Endianness; H Hasher; auto SwappedData = llvm::support::endian::byte_swap(Data, E); Hasher.update(llvm::ArrayRef( reinterpret_cast(&SwappedData), sizeof(Data))); return Hasher.final(); }; char C = 'c'; int32_t I = 0x12345678; uint64_t UI64 = static_cast(1) << 50; enum TestEnumeration : uint16_t { TE_One = 1, TE_Two = 2 }; TestEnumeration Enum = TE_Two; EXPECT_EQ(ByteSwapAndHashWithHasher(C), hashWithBuilder(C)); EXPECT_EQ(ByteSwapAndHashWithHasher(I), hashWithBuilder(I)); EXPECT_EQ(ByteSwapAndHashWithHasher(UI64), hashWithBuilder(UI64)); EXPECT_EQ(ByteSwapAndHashWithHasher(Enum), hashWithBuilder(Enum)); } struct SimpleStruct { char C; int I; }; template void addHash(llvm::HashBuilder &HBuilder, const SimpleStruct &Value) { HBuilder.add(Value.C); HBuilder.add(Value.I); } struct StructWithoutCopyOrMove { int I; StructWithoutCopyOrMove() = default; explicit StructWithoutCopyOrMove(int I) : I(I) {} StructWithoutCopyOrMove(const StructWithoutCopyOrMove &) = delete; StructWithoutCopyOrMove &operator=(const StructWithoutCopyOrMove &) = delete; template friend void addHash(llvm::HashBuilder &HBuilder, const StructWithoutCopyOrMove &Value) { HBuilder.add(Value.I); } }; // The struct and associated tests are simplified to avoid failures caused by // different alignments on different platforms. struct /* __attribute__((packed)) */ StructWithFastHash { int I; // char C; // If possible, we want to hash both `I` and `C` in a single `update` // call for performance concerns. template friend void addHash(llvm::HashBuilder &HBuilder, const StructWithFastHash &Value) { if (Endianness == llvm::endianness::native) { HBuilder.update(llvm::ArrayRef(reinterpret_cast(&Value), sizeof(Value))); } else { // Rely on existing `add` methods to handle endianness. HBuilder.add(Value.I); // HBuilder.add(Value.C); } } }; struct CustomContainer { private: size_t Size; int Elements[100]; public: CustomContainer(size_t Size) : Size(Size) { for (size_t I = 0; I != Size; ++I) Elements[I] = I; } template friend void addHash(llvm::HashBuilder &HBuilder, const CustomContainer &Value) { if (Endianness == llvm::endianness::native) { HBuilder.update(llvm::ArrayRef( reinterpret_cast(&Value.Size), sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0]))); } else { HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + Value.Size); } } }; TYPED_TEST(HashBuilderTest, HashUserDefinedStruct) { using HE = TypeParam; EXPECT_EQ(hashWithBuilder(SimpleStruct{'c', 123}), hashWithBuilder('c', 123)); EXPECT_EQ(hashWithBuilder(StructWithoutCopyOrMove{1}), hashWithBuilder(1)); EXPECT_EQ(hashWithBuilder(StructWithFastHash{123}), hashWithBuilder(123)); EXPECT_EQ(hashWithBuilder(CustomContainer(3)), hashWithBuilder(static_cast(3), 0, 1, 2)); } TYPED_TEST(HashBuilderTest, HashArrayRefHashableDataTypes) { using HE = TypeParam; int Values[] = {1, 20, 0x12345678}; llvm::ArrayRef Array(Values); EXPECT_NE(hashWithBuilder(Array), hashWithBuilder(1, 20, 0x12345678)); EXPECT_EQ(hashWithBuilder(Array), hashRangeWithBuilder(Array.begin(), Array.end())); EXPECT_EQ( hashWithBuilder(Array), hashRangeWithBuilder(Array.data(), Array.data() + Array.size())); } TYPED_TEST(HashBuilderTest, HashArrayRef) { using HE = TypeParam; int Values[] = {1, 2, 3}; llvm::ArrayRef Array123(&Values[0], 3); llvm::ArrayRef Array12(&Values[0], 2); llvm::ArrayRef Array1(&Values[0], 1); llvm::ArrayRef Array23(&Values[1], 2); llvm::ArrayRef Array3(&Values[2], 1); llvm::ArrayRef ArrayEmpty(&Values[0], static_cast(0)); auto Hash123andEmpty = hashWithBuilder(Array123, ArrayEmpty); auto Hash12And3 = hashWithBuilder(Array12, Array3); auto Hash1And23 = hashWithBuilder(Array1, Array23); auto HashEmptyAnd123 = hashWithBuilder(ArrayEmpty, Array123); EXPECT_NE(Hash123andEmpty, Hash12And3); EXPECT_NE(Hash123andEmpty, Hash1And23); EXPECT_NE(Hash123andEmpty, HashEmptyAnd123); EXPECT_NE(Hash12And3, Hash1And23); EXPECT_NE(Hash12And3, HashEmptyAnd123); EXPECT_NE(Hash1And23, HashEmptyAnd123); } TYPED_TEST(HashBuilderTest, HashArrayRefNonHashableDataTypes) { using HE = TypeParam; SimpleStruct Values[] = {{'a', 100}, {'b', 200}}; llvm::ArrayRef Array(Values); EXPECT_NE( hashWithBuilder(Array), hashWithBuilder(SimpleStruct{'a', 100}, SimpleStruct{'b', 200})); } TYPED_TEST(HashBuilderTest, HashStringRef) { using HE = TypeParam; llvm::StringRef SEmpty(""); llvm::StringRef S1("1"); llvm::StringRef S12("12"); llvm::StringRef S123("123"); llvm::StringRef S23("23"); llvm::StringRef S3("3"); auto Hash123andEmpty = hashWithBuilder(S123, SEmpty); auto Hash12And3 = hashWithBuilder(S12, S3); auto Hash1And23 = hashWithBuilder(S1, S23); auto HashEmptyAnd123 = hashWithBuilder(SEmpty, S123); EXPECT_NE(Hash123andEmpty, Hash12And3); EXPECT_NE(Hash123andEmpty, Hash1And23); EXPECT_NE(Hash123andEmpty, HashEmptyAnd123); EXPECT_NE(Hash12And3, Hash1And23); EXPECT_NE(Hash12And3, HashEmptyAnd123); EXPECT_NE(Hash1And23, HashEmptyAnd123); } TYPED_TEST(HashBuilderTest, HashStdString) { using HE = TypeParam; EXPECT_EQ(hashWithBuilder(std::string("123")), hashWithBuilder(llvm::StringRef("123"))); } TYPED_TEST(HashBuilderTest, HashStdPair) { using HE = TypeParam; EXPECT_EQ(hashWithBuilder(std::make_pair(1, "string")), hashWithBuilder(1, "string")); std::pair Pair; Pair.first.I = 1; Pair.second = "string"; EXPECT_EQ(hashWithBuilder(Pair), hashWithBuilder(1, "string")); } TYPED_TEST(HashBuilderTest, HashStdTuple) { using HE = TypeParam; EXPECT_EQ(hashWithBuilder(std::make_tuple(1)), hashWithBuilder(1)); EXPECT_EQ(hashWithBuilder(std::make_tuple(2ULL)), hashWithBuilder(2ULL)); EXPECT_EQ(hashWithBuilder(std::make_tuple("three")), hashWithBuilder("three")); EXPECT_EQ(hashWithBuilder(std::make_tuple(1, 2ULL)), hashWithBuilder(1, 2ULL)); EXPECT_EQ(hashWithBuilder(std::make_tuple(1, 2ULL, "three")), hashWithBuilder(1, 2ULL, "three")); std::tuple Tuple; std::get<0>(Tuple).I = 1; std::get<1>(Tuple) = "two"; EXPECT_EQ(hashWithBuilder(Tuple), hashWithBuilder(1, "two")); } TYPED_TEST(HashBuilderTest, HashRangeWithForwardIterator) { using HE = TypeParam; std::list List; List.push_back(1); List.push_back(2); List.push_back(3); EXPECT_NE(hashRangeWithBuilder(List), hashWithBuilder(1, 2, 3)); } TEST(CustomHasher, CustomHasher) { struct SumHash { explicit SumHash(uint8_t Seed1, uint8_t Seed2) : Hash(Seed1 + Seed2) {} void update(llvm::ArrayRef Data) { for (uint8_t C : Data) Hash += C; } uint8_t Hash; }; { llvm::HashBuilder HBuilder(0, 1); EXPECT_EQ(HBuilder.add(0x02, 0x03, 0x400).getHasher().Hash, 0xa); } { llvm::HashBuilder HBuilder(2, 3); EXPECT_EQ(HBuilder.add("ab", 'c').getHasher().Hash, static_cast(/*seeds*/ 2 + 3 + /*range size*/ 2 + /*characters*/ 'a' + 'b' + 'c')); } }