1 //===- llvm/unittest/Support/HashBuilderTest.cpp - HashBuilder unit tests -===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Support/HashBuilder.h" 10 #include "llvm/ADT/ArrayRef.h" 11 #include "llvm/Support/MD5.h" 12 #include "llvm/Support/SHA1.h" 13 #include "llvm/Support/SHA256.h" 14 #include "gtest/gtest.h" 15 16 #include <list> 17 #include <string> 18 #include <type_traits> 19 #include <utility> 20 #include <vector> 21 22 // gtest utilities and macros rely on using a single type. So wrap both the 23 // hasher type and endianness. 24 template <typename _HasherT, llvm::support::endianness _Endianness> 25 struct HasherTAndEndianness { 26 using HasherT = _HasherT; 27 static constexpr llvm::support::endianness Endianness = _Endianness; 28 }; 29 using HasherTAndEndiannessToTest = 30 ::testing::Types<HasherTAndEndianness<llvm::MD5, llvm::support::big>, 31 HasherTAndEndianness<llvm::MD5, llvm::support::little>, 32 HasherTAndEndianness<llvm::MD5, llvm::support::native>, 33 HasherTAndEndianness<llvm::SHA1, llvm::support::big>, 34 HasherTAndEndianness<llvm::SHA1, llvm::support::little>, 35 HasherTAndEndianness<llvm::SHA1, llvm::support::native>, 36 HasherTAndEndianness<llvm::SHA256, llvm::support::big>, 37 HasherTAndEndianness<llvm::SHA256, llvm::support::little>, 38 HasherTAndEndianness<llvm::SHA256, llvm::support::native>>; 39 template <typename HasherT> class HashBuilderTest : public testing::Test {}; 40 TYPED_TEST_SUITE(HashBuilderTest, HasherTAndEndiannessToTest, ); 41 42 template <typename HasherTAndEndianness> 43 using HashBuilder = llvm::HashBuilder<typename HasherTAndEndianness::HasherT, 44 HasherTAndEndianness::Endianness>; 45 46 template <typename HasherTAndEndianness, typename... Ts> 47 static typename HashBuilder<HasherTAndEndianness>::template HashResultTy<> 48 hashWithBuilder(const Ts &...Args) { 49 return HashBuilder<HasherTAndEndianness>().add(Args...).final(); 50 } 51 52 template <typename HasherTAndEndianness, typename... Ts> 53 static typename HashBuilder<HasherTAndEndianness>::template HashResultTy<> 54 hashRangeWithBuilder(const Ts &...Args) { 55 return HashBuilder<HasherTAndEndianness>().addRange(Args...).final(); 56 } 57 58 // All the test infrastructure relies on the variadic helpers. Test them first. 59 TYPED_TEST(HashBuilderTest, VariadicHelpers) { 60 { 61 HashBuilder<TypeParam> HBuilder; 62 63 HBuilder.add(100); 64 HBuilder.add('c'); 65 HBuilder.add("string"); 66 67 EXPECT_EQ(HBuilder.final(), hashWithBuilder<TypeParam>(100, 'c', "string")); 68 } 69 70 { 71 HashBuilder<TypeParam> HBuilder; 72 73 std::vector<int> Vec{100, 101, 102}; 74 HBuilder.addRange(Vec); 75 76 EXPECT_EQ(HBuilder.final(), hashRangeWithBuilder<TypeParam>(Vec)); 77 } 78 79 { 80 HashBuilder<TypeParam> HBuilder; 81 82 std::vector<int> Vec{200, 201, 202}; 83 HBuilder.addRange(Vec.begin(), Vec.end()); 84 85 EXPECT_EQ(HBuilder.final(), 86 hashRangeWithBuilder<TypeParam>(Vec.begin(), Vec.end())); 87 } 88 } 89 90 TYPED_TEST(HashBuilderTest, AddRangeElements) { 91 HashBuilder<TypeParam> HBuilder; 92 int Values[] = {1, 2, 3}; 93 HBuilder.addRangeElements(llvm::ArrayRef<int>(Values)); 94 EXPECT_EQ(HBuilder.final(), hashWithBuilder<TypeParam>(1, 2, 3)); 95 } 96 97 TYPED_TEST(HashBuilderTest, AddHashableData) { 98 using HE = TypeParam; 99 100 auto ByteSwapAndHashWithHasher = [](auto Data) { 101 using H = typename HE::HasherT; 102 constexpr auto E = HE::Endianness; 103 H Hasher; 104 auto SwappedData = llvm::support::endian::byte_swap(Data, E); 105 Hasher.update(llvm::makeArrayRef( 106 reinterpret_cast<const uint8_t *>(&SwappedData), sizeof(Data))); 107 return Hasher.final(); 108 }; 109 110 char C = 'c'; 111 int32_t I = 0x12345678; 112 uint64_t UI64 = static_cast<uint64_t>(1) << 50; 113 enum TestEnumeration : uint16_t { TE_One = 1, TE_Two = 2 }; 114 TestEnumeration Enum = TE_Two; 115 116 EXPECT_EQ(ByteSwapAndHashWithHasher(C), hashWithBuilder<HE>(C)); 117 EXPECT_EQ(ByteSwapAndHashWithHasher(I), hashWithBuilder<HE>(I)); 118 EXPECT_EQ(ByteSwapAndHashWithHasher(UI64), hashWithBuilder<HE>(UI64)); 119 EXPECT_EQ(ByteSwapAndHashWithHasher(Enum), hashWithBuilder<HE>(Enum)); 120 } 121 122 struct SimpleStruct { 123 char C; 124 int I; 125 }; 126 127 template <typename HasherT, llvm::support::endianness Endianness> 128 void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, 129 const SimpleStruct &Value) { 130 HBuilder.add(Value.C); 131 HBuilder.add(Value.I); 132 } 133 134 struct StructWithoutCopyOrMove { 135 int I; 136 StructWithoutCopyOrMove() = default; 137 StructWithoutCopyOrMove(const StructWithoutCopyOrMove &) = delete; 138 StructWithoutCopyOrMove &operator=(const StructWithoutCopyOrMove &) = delete; 139 140 template <typename HasherT, llvm::support::endianness Endianness> 141 friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, 142 const StructWithoutCopyOrMove &Value) { 143 HBuilder.add(Value.I); 144 } 145 }; 146 147 // The struct and associated tests are simplified to avoid failures caused by 148 // different alignments on different platforms. 149 struct /* __attribute__((packed)) */ StructWithFastHash { 150 int I; 151 // char C; 152 153 // If possible, we want to hash both `I` and `C` in a single `update` 154 // call for performance concerns. 155 template <typename HasherT, llvm::support::endianness Endianness> 156 friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, 157 const StructWithFastHash &Value) { 158 if (Endianness == llvm::support::endian::system_endianness()) { 159 HBuilder.update(llvm::makeArrayRef( 160 reinterpret_cast<const uint8_t *>(&Value), sizeof(Value))); 161 } else { 162 // Rely on existing `add` methods to handle endianness. 163 HBuilder.add(Value.I); 164 // HBuilder.add(Value.C); 165 } 166 } 167 }; 168 169 struct CustomContainer { 170 private: 171 size_t Size; 172 int Elements[100]; 173 174 public: 175 CustomContainer(size_t Size) : Size(Size) { 176 for (size_t I = 0; I != Size; ++I) 177 Elements[I] = I; 178 } 179 template <typename HasherT, llvm::support::endianness Endianness> 180 friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, 181 const CustomContainer &Value) { 182 if (Endianness == llvm::support::endian::system_endianness()) { 183 HBuilder.update(llvm::makeArrayRef( 184 reinterpret_cast<const uint8_t *>(&Value.Size), 185 sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0]))); 186 } else { 187 HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + Value.Size); 188 } 189 } 190 }; 191 192 TYPED_TEST(HashBuilderTest, HashUserDefinedStruct) { 193 using HE = TypeParam; 194 EXPECT_EQ(hashWithBuilder<HE>(SimpleStruct{'c', 123}), 195 hashWithBuilder<HE>('c', 123)); 196 EXPECT_EQ(hashWithBuilder<HE>(StructWithoutCopyOrMove{1}), 197 hashWithBuilder<HE>(1)); 198 EXPECT_EQ(hashWithBuilder<HE>(StructWithFastHash{123}), 199 hashWithBuilder<HE>(123)); 200 EXPECT_EQ(hashWithBuilder<HE>(CustomContainer(3)), 201 hashWithBuilder<HE>(static_cast<size_t>(3), 0, 1, 2)); 202 } 203 204 TYPED_TEST(HashBuilderTest, HashArrayRefHashableDataTypes) { 205 using HE = TypeParam; 206 int Values[] = {1, 20, 0x12345678}; 207 llvm::ArrayRef<int> Array(Values); 208 EXPECT_NE(hashWithBuilder<HE>(Array), hashWithBuilder<HE>(1, 20, 0x12345678)); 209 EXPECT_EQ(hashWithBuilder<HE>(Array), 210 hashRangeWithBuilder<HE>(Array.begin(), Array.end())); 211 EXPECT_EQ( 212 hashWithBuilder<HE>(Array), 213 hashRangeWithBuilder<HE>(Array.data(), Array.data() + Array.size())); 214 } 215 216 TYPED_TEST(HashBuilderTest, HashArrayRef) { 217 using HE = TypeParam; 218 int Values[] = {1, 2, 3}; 219 llvm::ArrayRef<int> Array123(&Values[0], 3); 220 llvm::ArrayRef<int> Array12(&Values[0], 2); 221 llvm::ArrayRef<int> Array1(&Values[0], 1); 222 llvm::ArrayRef<int> Array23(&Values[1], 2); 223 llvm::ArrayRef<int> Array3(&Values[2], 1); 224 llvm::ArrayRef<int> ArrayEmpty(&Values[0], static_cast<size_t>(0)); 225 226 auto Hash123andEmpty = hashWithBuilder<HE>(Array123, ArrayEmpty); 227 auto Hash12And3 = hashWithBuilder<HE>(Array12, Array3); 228 auto Hash1And23 = hashWithBuilder<HE>(Array1, Array23); 229 auto HashEmptyAnd123 = hashWithBuilder<HE>(ArrayEmpty, Array123); 230 231 EXPECT_NE(Hash123andEmpty, Hash12And3); 232 EXPECT_NE(Hash123andEmpty, Hash1And23); 233 EXPECT_NE(Hash123andEmpty, HashEmptyAnd123); 234 EXPECT_NE(Hash12And3, Hash1And23); 235 EXPECT_NE(Hash12And3, HashEmptyAnd123); 236 EXPECT_NE(Hash1And23, HashEmptyAnd123); 237 } 238 239 TYPED_TEST(HashBuilderTest, HashArrayRefNonHashableDataTypes) { 240 using HE = TypeParam; 241 SimpleStruct Values[] = {{'a', 100}, {'b', 200}}; 242 llvm::ArrayRef<SimpleStruct> Array(Values); 243 EXPECT_NE( 244 hashWithBuilder<HE>(Array), 245 hashWithBuilder<HE>(SimpleStruct{'a', 100}, SimpleStruct{'b', 200})); 246 } 247 248 TYPED_TEST(HashBuilderTest, HashStringRef) { 249 using HE = TypeParam; 250 llvm::StringRef SEmpty(""); 251 llvm::StringRef S1("1"); 252 llvm::StringRef S12("12"); 253 llvm::StringRef S123("123"); 254 llvm::StringRef S23("23"); 255 llvm::StringRef S3("3"); 256 257 auto Hash123andEmpty = hashWithBuilder<HE>(S123, SEmpty); 258 auto Hash12And3 = hashWithBuilder<HE>(S12, S3); 259 auto Hash1And23 = hashWithBuilder<HE>(S1, S23); 260 auto HashEmptyAnd123 = hashWithBuilder<HE>(SEmpty, S123); 261 262 EXPECT_NE(Hash123andEmpty, Hash12And3); 263 EXPECT_NE(Hash123andEmpty, Hash1And23); 264 EXPECT_NE(Hash123andEmpty, HashEmptyAnd123); 265 EXPECT_NE(Hash12And3, Hash1And23); 266 EXPECT_NE(Hash12And3, HashEmptyAnd123); 267 EXPECT_NE(Hash1And23, HashEmptyAnd123); 268 } 269 270 TYPED_TEST(HashBuilderTest, HashStdString) { 271 using HE = TypeParam; 272 EXPECT_EQ(hashWithBuilder<HE>(std::string("123")), 273 hashWithBuilder<HE>(llvm::StringRef("123"))); 274 } 275 276 TYPED_TEST(HashBuilderTest, HashStdPair) { 277 using HE = TypeParam; 278 EXPECT_EQ(hashWithBuilder<HE>(std::make_pair(1, "string")), 279 hashWithBuilder<HE>(1, "string")); 280 281 std::pair<StructWithoutCopyOrMove, std::string> Pair; 282 Pair.first.I = 1; 283 Pair.second = "string"; 284 EXPECT_EQ(hashWithBuilder<HE>(Pair), hashWithBuilder<HE>(1, "string")); 285 } 286 287 TYPED_TEST(HashBuilderTest, HashStdTuple) { 288 using HE = TypeParam; 289 290 EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1)), hashWithBuilder<HE>(1)); 291 EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(2ULL)), 292 hashWithBuilder<HE>(2ULL)); 293 EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple("three")), 294 hashWithBuilder<HE>("three")); 295 EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1, 2ULL)), 296 hashWithBuilder<HE>(1, 2ULL)); 297 EXPECT_EQ(hashWithBuilder<HE>(std::make_tuple(1, 2ULL, "three")), 298 hashWithBuilder<HE>(1, 2ULL, "three")); 299 300 std::tuple<StructWithoutCopyOrMove, std::string> Tuple; 301 std::get<0>(Tuple).I = 1; 302 std::get<1>(Tuple) = "two"; 303 304 EXPECT_EQ(hashWithBuilder<HE>(Tuple), hashWithBuilder<HE>(1, "two")); 305 } 306 307 TYPED_TEST(HashBuilderTest, HashRangeWithForwardIterator) { 308 using HE = TypeParam; 309 std::list<int> List; 310 List.push_back(1); 311 List.push_back(2); 312 List.push_back(3); 313 EXPECT_NE(hashRangeWithBuilder<HE>(List), hashWithBuilder<HE>(1, 2, 3)); 314 } 315 316 TEST(CustomHasher, CustomHasher) { 317 struct SumHash { 318 explicit SumHash(uint8_t Seed1, uint8_t Seed2) : Hash(Seed1 + Seed2) {} 319 void update(llvm::ArrayRef<uint8_t> Data) { 320 for (uint8_t C : Data) 321 Hash += C; 322 } 323 uint8_t Hash; 324 }; 325 326 { 327 llvm::HashBuilder<SumHash, llvm::support::endianness::little> HBuilder(0, 328 1); 329 EXPECT_EQ(HBuilder.add(0x02, 0x03, 0x400).getHasher().Hash, 0xa); 330 } 331 { 332 llvm::HashBuilder<SumHash, llvm::support::endianness::little> HBuilder(2, 333 3); 334 EXPECT_EQ(HBuilder.add("ab", 'c').getHasher().Hash, 335 static_cast<uint8_t>(/*seeds*/ 2 + 3 + /*range size*/ 2 + 336 /*characters*/ 'a' + 'b' + 'c')); 337 } 338 } 339