1*349cc55cSDimitry Andric //===- llvm/Support/HashBuilder.h - Convenient hashing interface-*- C++ -*-===// 2*349cc55cSDimitry Andric // 3*349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*349cc55cSDimitry Andric // 7*349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8*349cc55cSDimitry Andric // 9*349cc55cSDimitry Andric // This file implements an interface allowing to conveniently build hashes of 10*349cc55cSDimitry Andric // various data types, without relying on the underlying hasher type to know 11*349cc55cSDimitry Andric // about hashed data types. 12*349cc55cSDimitry Andric // 13*349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 14*349cc55cSDimitry Andric 15*349cc55cSDimitry Andric #ifndef LLVM_SUPPORT_HASHBUILDER_H 16*349cc55cSDimitry Andric #define LLVM_SUPPORT_HASHBUILDER_H 17*349cc55cSDimitry Andric 18*349cc55cSDimitry Andric #include "llvm/ADT/ArrayRef.h" 19*349cc55cSDimitry Andric #include "llvm/ADT/Hashing.h" 20*349cc55cSDimitry Andric #include "llvm/ADT/STLExtras.h" 21*349cc55cSDimitry Andric #include "llvm/ADT/StringRef.h" 22*349cc55cSDimitry Andric #include "llvm/Support/Endian.h" 23*349cc55cSDimitry Andric #include "llvm/Support/type_traits.h" 24*349cc55cSDimitry Andric 25*349cc55cSDimitry Andric #include <iterator> 26*349cc55cSDimitry Andric #include <utility> 27*349cc55cSDimitry Andric 28*349cc55cSDimitry Andric namespace llvm { 29*349cc55cSDimitry Andric 30*349cc55cSDimitry Andric namespace hashbuilder_detail { 31*349cc55cSDimitry Andric /// Trait to indicate whether a type's bits can be hashed directly (after 32*349cc55cSDimitry Andric /// endianness correction). 33*349cc55cSDimitry Andric template <typename U> 34*349cc55cSDimitry Andric struct IsHashableData 35*349cc55cSDimitry Andric : std::integral_constant<bool, is_integral_or_enum<U>::value> {}; 36*349cc55cSDimitry Andric 37*349cc55cSDimitry Andric } // namespace hashbuilder_detail 38*349cc55cSDimitry Andric 39*349cc55cSDimitry Andric /// Declares the hasher member, and functions forwarding directly to the hasher. 40*349cc55cSDimitry Andric template <typename HasherT> class HashBuilderBase { 41*349cc55cSDimitry Andric public: 42*349cc55cSDimitry Andric HasherT &getHasher() { return Hasher; } 43*349cc55cSDimitry Andric 44*349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 45*349cc55cSDimitry Andric /// 46*349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 47*349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 48*349cc55cSDimitry Andric /// contraints. 49*349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { this->getHasher().update(Data); } 50*349cc55cSDimitry Andric 51*349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 52*349cc55cSDimitry Andric /// 53*349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 54*349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 55*349cc55cSDimitry Andric /// contraints. 56*349cc55cSDimitry Andric void update(StringRef Data) { 57*349cc55cSDimitry Andric update(makeArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), 58*349cc55cSDimitry Andric Data.size())); 59*349cc55cSDimitry Andric } 60*349cc55cSDimitry Andric 61*349cc55cSDimitry Andric /// Forward to `HasherT::final()` if available. 62*349cc55cSDimitry Andric template <typename HasherT_ = HasherT> StringRef final() { 63*349cc55cSDimitry Andric return this->getHasher().final(); 64*349cc55cSDimitry Andric } 65*349cc55cSDimitry Andric 66*349cc55cSDimitry Andric /// Forward to `HasherT::result()` if available. 67*349cc55cSDimitry Andric template <typename HasherT_ = HasherT> StringRef result() { 68*349cc55cSDimitry Andric return this->getHasher().result(); 69*349cc55cSDimitry Andric } 70*349cc55cSDimitry Andric 71*349cc55cSDimitry Andric protected: 72*349cc55cSDimitry Andric explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {} 73*349cc55cSDimitry Andric 74*349cc55cSDimitry Andric template <typename... ArgTypes> 75*349cc55cSDimitry Andric explicit HashBuilderBase(ArgTypes &&...Args) 76*349cc55cSDimitry Andric : OptionalHasher(in_place, std::forward<ArgTypes>(Args)...), 77*349cc55cSDimitry Andric Hasher(*OptionalHasher) {} 78*349cc55cSDimitry Andric 79*349cc55cSDimitry Andric private: 80*349cc55cSDimitry Andric Optional<HasherT> OptionalHasher; 81*349cc55cSDimitry Andric HasherT &Hasher; 82*349cc55cSDimitry Andric }; 83*349cc55cSDimitry Andric 84*349cc55cSDimitry Andric /// Implementation of the `HashBuilder` interface. 85*349cc55cSDimitry Andric /// 86*349cc55cSDimitry Andric /// `support::endianness::native` is not supported. `HashBuilder` is 87*349cc55cSDimitry Andric /// expected to canonicalize `support::endianness::native` to one of 88*349cc55cSDimitry Andric /// `support::endianness::big` or `support::endianness::little`. 89*349cc55cSDimitry Andric template <typename HasherT, support::endianness Endianness> 90*349cc55cSDimitry Andric class HashBuilderImpl : public HashBuilderBase<HasherT> { 91*349cc55cSDimitry Andric static_assert(Endianness != support::endianness::native, 92*349cc55cSDimitry Andric "HashBuilder should canonicalize endianness"); 93*349cc55cSDimitry Andric 94*349cc55cSDimitry Andric public: 95*349cc55cSDimitry Andric explicit HashBuilderImpl(HasherT &Hasher) 96*349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Hasher) {} 97*349cc55cSDimitry Andric template <typename... ArgTypes> 98*349cc55cSDimitry Andric explicit HashBuilderImpl(ArgTypes &&...Args) 99*349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Args...) {} 100*349cc55cSDimitry Andric 101*349cc55cSDimitry Andric /// Implement hashing for hashable data types, e.g. integral or enum values. 102*349cc55cSDimitry Andric template <typename T> 103*349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, 104*349cc55cSDimitry Andric HashBuilderImpl &> 105*349cc55cSDimitry Andric add(T Value) { 106*349cc55cSDimitry Andric return adjustForEndiannessAndAdd(Value); 107*349cc55cSDimitry Andric } 108*349cc55cSDimitry Andric 109*349cc55cSDimitry Andric /// Support hashing `ArrayRef`. 110*349cc55cSDimitry Andric /// 111*349cc55cSDimitry Andric /// `Value.size()` is taken into account to ensure cases like 112*349cc55cSDimitry Andric /// ``` 113*349cc55cSDimitry Andric /// builder.add({1}); 114*349cc55cSDimitry Andric /// builder.add({2, 3}); 115*349cc55cSDimitry Andric /// ``` 116*349cc55cSDimitry Andric /// and 117*349cc55cSDimitry Andric /// ``` 118*349cc55cSDimitry Andric /// builder.add({1, 2}); 119*349cc55cSDimitry Andric /// builder.add({3}); 120*349cc55cSDimitry Andric /// ``` 121*349cc55cSDimitry Andric /// do not collide. 122*349cc55cSDimitry Andric template <typename T> HashBuilderImpl &add(ArrayRef<T> Value) { 123*349cc55cSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go 124*349cc55cSDimitry Andric // through the `update` fast path. But that would rely on the implementation 125*349cc55cSDimitry Andric // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call 126*349cc55cSDimitry Andric // `update` to guarantee the fast path. 127*349cc55cSDimitry Andric add(Value.size()); 128*349cc55cSDimitry Andric if (hashbuilder_detail::IsHashableData<T>::value && 129*349cc55cSDimitry Andric Endianness == support::endian::system_endianness()) { 130*349cc55cSDimitry Andric this->update( 131*349cc55cSDimitry Andric makeArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), 132*349cc55cSDimitry Andric Value.size() * sizeof(T))); 133*349cc55cSDimitry Andric } else { 134*349cc55cSDimitry Andric for (auto &V : Value) 135*349cc55cSDimitry Andric add(V); 136*349cc55cSDimitry Andric } 137*349cc55cSDimitry Andric return *this; 138*349cc55cSDimitry Andric } 139*349cc55cSDimitry Andric 140*349cc55cSDimitry Andric /// Support hashing `StringRef`. 141*349cc55cSDimitry Andric /// 142*349cc55cSDimitry Andric /// `Value.size()` is taken into account to ensure cases like 143*349cc55cSDimitry Andric /// ``` 144*349cc55cSDimitry Andric /// builder.add("a"); 145*349cc55cSDimitry Andric /// builder.add("bc"); 146*349cc55cSDimitry Andric /// ``` 147*349cc55cSDimitry Andric /// and 148*349cc55cSDimitry Andric /// ``` 149*349cc55cSDimitry Andric /// builder.add("ab"); 150*349cc55cSDimitry Andric /// builder.add("c"); 151*349cc55cSDimitry Andric /// ``` 152*349cc55cSDimitry Andric /// do not collide. 153*349cc55cSDimitry Andric HashBuilderImpl &add(StringRef Value) { 154*349cc55cSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go 155*349cc55cSDimitry Andric // through `update`. But that would rely on the implementation of 156*349cc55cSDimitry Andric // `StringRef::begin()` and `StringRef::end()`. Explicitly call `update` to 157*349cc55cSDimitry Andric // guarantee the fast path. 158*349cc55cSDimitry Andric add(Value.size()); 159*349cc55cSDimitry Andric this->update(makeArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), 160*349cc55cSDimitry Andric Value.size())); 161*349cc55cSDimitry Andric return *this; 162*349cc55cSDimitry Andric } 163*349cc55cSDimitry Andric 164*349cc55cSDimitry Andric template <typename T> 165*349cc55cSDimitry Andric using HasAddHashT = 166*349cc55cSDimitry Andric decltype(addHash(std::declval<HashBuilderImpl &>(), std::declval<T &>())); 167*349cc55cSDimitry Andric /// Implement hashing for user-defined `struct`s. 168*349cc55cSDimitry Andric /// 169*349cc55cSDimitry Andric /// Any user-define `struct` can participate in hashing via `HashBuilder` by 170*349cc55cSDimitry Andric /// providing a `addHash` templated function. 171*349cc55cSDimitry Andric /// 172*349cc55cSDimitry Andric /// ``` 173*349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 174*349cc55cSDimitry Andric /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder, 175*349cc55cSDimitry Andric /// const UserDefinedStruct &Value); 176*349cc55cSDimitry Andric /// ``` 177*349cc55cSDimitry Andric /// 178*349cc55cSDimitry Andric /// For example: 179*349cc55cSDimitry Andric /// ``` 180*349cc55cSDimitry Andric /// struct SimpleStruct { 181*349cc55cSDimitry Andric /// char c; 182*349cc55cSDimitry Andric /// int i; 183*349cc55cSDimitry Andric /// }; 184*349cc55cSDimitry Andric /// 185*349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 186*349cc55cSDimitry Andric /// void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 187*349cc55cSDimitry Andric /// const SimpleStruct &Value) { 188*349cc55cSDimitry Andric /// HBuilder.add(Value.c); 189*349cc55cSDimitry Andric /// HBuilder.add(Value.i); 190*349cc55cSDimitry Andric /// } 191*349cc55cSDimitry Andric /// ``` 192*349cc55cSDimitry Andric /// 193*349cc55cSDimitry Andric /// To avoid endianness issues, specializations of `addHash` should 194*349cc55cSDimitry Andric /// generally rely on exising `add`, `addRange`, and `addRangeElements` 195*349cc55cSDimitry Andric /// functions. If directly using `update`, an implementation must correctly 196*349cc55cSDimitry Andric /// handle endianness. 197*349cc55cSDimitry Andric /// 198*349cc55cSDimitry Andric /// ``` 199*349cc55cSDimitry Andric /// struct __attribute__ ((packed)) StructWithFastHash { 200*349cc55cSDimitry Andric /// int I; 201*349cc55cSDimitry Andric /// char C; 202*349cc55cSDimitry Andric /// 203*349cc55cSDimitry Andric /// // If possible, we want to hash both `I` and `C` in a single 204*349cc55cSDimitry Andric /// // `update` call for performance concerns. 205*349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 206*349cc55cSDimitry Andric /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 207*349cc55cSDimitry Andric /// const StructWithFastHash &Value) { 208*349cc55cSDimitry Andric /// if (Endianness == support::endian::system_endianness()) { 209*349cc55cSDimitry Andric /// HBuilder.update(makeArrayRef( 210*349cc55cSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value), sizeof(Value))); 211*349cc55cSDimitry Andric /// } else { 212*349cc55cSDimitry Andric /// // Rely on existing `add` methods to handle endianness. 213*349cc55cSDimitry Andric /// HBuilder.add(Value.I); 214*349cc55cSDimitry Andric /// HBuilder.add(Value.C); 215*349cc55cSDimitry Andric /// } 216*349cc55cSDimitry Andric /// } 217*349cc55cSDimitry Andric /// }; 218*349cc55cSDimitry Andric /// ``` 219*349cc55cSDimitry Andric /// 220*349cc55cSDimitry Andric /// To avoid collisions, specialization of `addHash` for variable-size 221*349cc55cSDimitry Andric /// types must take the size into account. 222*349cc55cSDimitry Andric /// 223*349cc55cSDimitry Andric /// For example: 224*349cc55cSDimitry Andric /// ``` 225*349cc55cSDimitry Andric /// struct CustomContainer { 226*349cc55cSDimitry Andric /// private: 227*349cc55cSDimitry Andric /// size_t Size; 228*349cc55cSDimitry Andric /// int Elements[100]; 229*349cc55cSDimitry Andric /// 230*349cc55cSDimitry Andric /// public: 231*349cc55cSDimitry Andric /// CustomContainer(size_t Size) : Size(Size) { 232*349cc55cSDimitry Andric /// for (size_t I = 0; I != Size; ++I) 233*349cc55cSDimitry Andric /// Elements[I] = I; 234*349cc55cSDimitry Andric /// } 235*349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 236*349cc55cSDimitry Andric /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 237*349cc55cSDimitry Andric /// const CustomContainer &Value) { 238*349cc55cSDimitry Andric /// if (Endianness == support::endian::system_endianness()) { 239*349cc55cSDimitry Andric /// HBuilder.update(makeArrayRef( 240*349cc55cSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value.Size), 241*349cc55cSDimitry Andric /// sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0]))); 242*349cc55cSDimitry Andric /// } else { 243*349cc55cSDimitry Andric /// // `addRange` will take care of encoding the size. 244*349cc55cSDimitry Andric /// HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + 245*349cc55cSDimitry Andric /// Value.Size); 246*349cc55cSDimitry Andric /// } 247*349cc55cSDimitry Andric /// } 248*349cc55cSDimitry Andric /// }; 249*349cc55cSDimitry Andric /// ``` 250*349cc55cSDimitry Andric template <typename T> 251*349cc55cSDimitry Andric std::enable_if_t<is_detected<HasAddHashT, T>::value && 252*349cc55cSDimitry Andric !hashbuilder_detail::IsHashableData<T>::value, 253*349cc55cSDimitry Andric HashBuilderImpl &> 254*349cc55cSDimitry Andric add(const T &Value) { 255*349cc55cSDimitry Andric addHash(*this, Value); 256*349cc55cSDimitry Andric return *this; 257*349cc55cSDimitry Andric } 258*349cc55cSDimitry Andric 259*349cc55cSDimitry Andric template <typename T1, typename T2> 260*349cc55cSDimitry Andric HashBuilderImpl &add(const std::pair<T1, T2> &Value) { 261*349cc55cSDimitry Andric add(Value.first); 262*349cc55cSDimitry Andric add(Value.second); 263*349cc55cSDimitry Andric return *this; 264*349cc55cSDimitry Andric } 265*349cc55cSDimitry Andric 266*349cc55cSDimitry Andric template <typename... Ts> HashBuilderImpl &add(const std::tuple<Ts...> &Arg) { 267*349cc55cSDimitry Andric return addTupleHelper(Arg, typename std::index_sequence_for<Ts...>()); 268*349cc55cSDimitry Andric } 269*349cc55cSDimitry Andric 270*349cc55cSDimitry Andric /// A convenenience variadic helper. 271*349cc55cSDimitry Andric /// It simply iterates over its arguments, in order. 272*349cc55cSDimitry Andric /// ``` 273*349cc55cSDimitry Andric /// add(Arg1, Arg2); 274*349cc55cSDimitry Andric /// ``` 275*349cc55cSDimitry Andric /// is equivalent to 276*349cc55cSDimitry Andric /// ``` 277*349cc55cSDimitry Andric /// add(Arg1) 278*349cc55cSDimitry Andric /// add(Arg2) 279*349cc55cSDimitry Andric /// ``` 280*349cc55cSDimitry Andric template <typename T, typename... Ts> 281*349cc55cSDimitry Andric typename std::enable_if<(sizeof...(Ts) >= 1), HashBuilderImpl &>::type 282*349cc55cSDimitry Andric add(const T &FirstArg, const Ts &...Args) { 283*349cc55cSDimitry Andric add(FirstArg); 284*349cc55cSDimitry Andric add(Args...); 285*349cc55cSDimitry Andric return *this; 286*349cc55cSDimitry Andric } 287*349cc55cSDimitry Andric 288*349cc55cSDimitry Andric template <typename ForwardIteratorT> 289*349cc55cSDimitry Andric HashBuilderImpl &addRange(ForwardIteratorT First, ForwardIteratorT Last) { 290*349cc55cSDimitry Andric add(std::distance(First, Last)); 291*349cc55cSDimitry Andric return addRangeElements(First, Last); 292*349cc55cSDimitry Andric } 293*349cc55cSDimitry Andric 294*349cc55cSDimitry Andric template <typename RangeT> HashBuilderImpl &addRange(const RangeT &Range) { 295*349cc55cSDimitry Andric return addRange(adl_begin(Range), adl_end(Range)); 296*349cc55cSDimitry Andric } 297*349cc55cSDimitry Andric 298*349cc55cSDimitry Andric template <typename ForwardIteratorT> 299*349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(ForwardIteratorT First, 300*349cc55cSDimitry Andric ForwardIteratorT Last) { 301*349cc55cSDimitry Andric return addRangeElementsImpl( 302*349cc55cSDimitry Andric First, Last, 303*349cc55cSDimitry Andric typename std::iterator_traits<ForwardIteratorT>::iterator_category()); 304*349cc55cSDimitry Andric } 305*349cc55cSDimitry Andric 306*349cc55cSDimitry Andric template <typename RangeT> 307*349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(const RangeT &Range) { 308*349cc55cSDimitry Andric return addRangeElements(adl_begin(Range), adl_end(Range)); 309*349cc55cSDimitry Andric } 310*349cc55cSDimitry Andric 311*349cc55cSDimitry Andric template <typename T> 312*349cc55cSDimitry Andric using HasByteSwapT = decltype(support::endian::byte_swap( 313*349cc55cSDimitry Andric std::declval<T &>(), support::endianness::little)); 314*349cc55cSDimitry Andric /// Adjust `Value` for the target endianness and add it to the hash. 315*349cc55cSDimitry Andric template <typename T> 316*349cc55cSDimitry Andric std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilderImpl &> 317*349cc55cSDimitry Andric adjustForEndiannessAndAdd(const T &Value) { 318*349cc55cSDimitry Andric T SwappedValue = support::endian::byte_swap(Value, Endianness); 319*349cc55cSDimitry Andric this->update(makeArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue), 320*349cc55cSDimitry Andric sizeof(SwappedValue))); 321*349cc55cSDimitry Andric return *this; 322*349cc55cSDimitry Andric } 323*349cc55cSDimitry Andric 324*349cc55cSDimitry Andric private: 325*349cc55cSDimitry Andric template <typename... Ts, std::size_t... Indices> 326*349cc55cSDimitry Andric HashBuilderImpl &addTupleHelper(const std::tuple<Ts...> &Arg, 327*349cc55cSDimitry Andric std::index_sequence<Indices...>) { 328*349cc55cSDimitry Andric add(std::get<Indices>(Arg)...); 329*349cc55cSDimitry Andric return *this; 330*349cc55cSDimitry Andric } 331*349cc55cSDimitry Andric 332*349cc55cSDimitry Andric // FIXME: Once available, specialize this function for `contiguous_iterator`s, 333*349cc55cSDimitry Andric // and use it for `ArrayRef` and `StringRef`. 334*349cc55cSDimitry Andric template <typename ForwardIteratorT> 335*349cc55cSDimitry Andric HashBuilderImpl &addRangeElementsImpl(ForwardIteratorT First, 336*349cc55cSDimitry Andric ForwardIteratorT Last, 337*349cc55cSDimitry Andric std::forward_iterator_tag) { 338*349cc55cSDimitry Andric for (auto It = First; It != Last; ++It) 339*349cc55cSDimitry Andric add(*It); 340*349cc55cSDimitry Andric return *this; 341*349cc55cSDimitry Andric } 342*349cc55cSDimitry Andric 343*349cc55cSDimitry Andric template <typename T> 344*349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value && 345*349cc55cSDimitry Andric Endianness == support::endian::system_endianness(), 346*349cc55cSDimitry Andric HashBuilderImpl &> 347*349cc55cSDimitry Andric addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) { 348*349cc55cSDimitry Andric this->update(makeArrayRef(reinterpret_cast<const uint8_t *>(First), 349*349cc55cSDimitry Andric (Last - First) * sizeof(T))); 350*349cc55cSDimitry Andric return *this; 351*349cc55cSDimitry Andric } 352*349cc55cSDimitry Andric }; 353*349cc55cSDimitry Andric 354*349cc55cSDimitry Andric /// Interface to help hash various types through a hasher type. 355*349cc55cSDimitry Andric /// 356*349cc55cSDimitry Andric /// Via provided specializations of `add`, `addRange`, and `addRangeElements` 357*349cc55cSDimitry Andric /// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed 358*349cc55cSDimitry Andric /// without requiring any knowledge of hashed types from the hasher type. 359*349cc55cSDimitry Andric /// 360*349cc55cSDimitry Andric /// The only method expected from the templated hasher type `HasherT` is: 361*349cc55cSDimitry Andric /// * void update(ArrayRef<uint8_t> Data) 362*349cc55cSDimitry Andric /// 363*349cc55cSDimitry Andric /// Additionally, the following methods will be forwarded to the hasher type: 364*349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().final()) final() 365*349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().result()) result() 366*349cc55cSDimitry Andric /// 367*349cc55cSDimitry Andric /// From a user point of view, the interface provides the following: 368*349cc55cSDimitry Andric /// * `template<typename T> add(const T &Value)` 369*349cc55cSDimitry Andric /// The `add` function implements hashing of various types. 370*349cc55cSDimitry Andric /// * `template <typename ItT> void addRange(ItT First, ItT Last)` 371*349cc55cSDimitry Andric /// The `addRange` function is designed to aid hashing a range of values. 372*349cc55cSDimitry Andric /// It explicitly adds the size of the range in the hash. 373*349cc55cSDimitry Andric /// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)` 374*349cc55cSDimitry Andric /// The `addRangeElements` function is also designed to aid hashing a range of 375*349cc55cSDimitry Andric /// values. In contrast to `addRange`, it **ignores** the size of the range, 376*349cc55cSDimitry Andric /// behaving as if elements were added one at a time with `add`. 377*349cc55cSDimitry Andric /// 378*349cc55cSDimitry Andric /// User-defined `struct` types can participate in this interface by providing 379*349cc55cSDimitry Andric /// an `addHash` templated function. See the associated template specialization 380*349cc55cSDimitry Andric /// for details. 381*349cc55cSDimitry Andric /// 382*349cc55cSDimitry Andric /// This interface does not impose requirements on the hasher 383*349cc55cSDimitry Andric /// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for 384*349cc55cSDimitry Andric /// variable-size types; for example for 385*349cc55cSDimitry Andric /// ``` 386*349cc55cSDimitry Andric /// builder.add({1}); 387*349cc55cSDimitry Andric /// builder.add({2, 3}); 388*349cc55cSDimitry Andric /// ``` 389*349cc55cSDimitry Andric /// and 390*349cc55cSDimitry Andric /// ``` 391*349cc55cSDimitry Andric /// builder.add({1, 2}); 392*349cc55cSDimitry Andric /// builder.add({3}); 393*349cc55cSDimitry Andric /// ``` 394*349cc55cSDimitry Andric /// . Thus, specializations of `add` and `addHash` for variable-size types must 395*349cc55cSDimitry Andric /// not assume that the hasher type considers the size as part of the hash; they 396*349cc55cSDimitry Andric /// must explicitly add the size to the hash. See for example specializations 397*349cc55cSDimitry Andric /// for `ArrayRef` and `StringRef`. 398*349cc55cSDimitry Andric /// 399*349cc55cSDimitry Andric /// Additionally, since types are eventually forwarded to the hasher's 400*349cc55cSDimitry Andric /// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash 401*349cc55cSDimitry Andric /// computation (for example when computing `add((int)123)`). 402*349cc55cSDimitry Andric /// Specifiying a non-`native` `Endianness` template parameter allows to compute 403*349cc55cSDimitry Andric /// stable hash across platforms with different endianness. 404*349cc55cSDimitry Andric template <class HasherT, support::endianness Endianness> 405*349cc55cSDimitry Andric using HashBuilder = 406*349cc55cSDimitry Andric HashBuilderImpl<HasherT, (Endianness == support::endianness::native 407*349cc55cSDimitry Andric ? support::endian::system_endianness() 408*349cc55cSDimitry Andric : Endianness)>; 409*349cc55cSDimitry Andric 410*349cc55cSDimitry Andric namespace hashbuilder_detail { 411*349cc55cSDimitry Andric class HashCodeHasher { 412*349cc55cSDimitry Andric public: 413*349cc55cSDimitry Andric HashCodeHasher() : Code(0) {} 414*349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { 415*349cc55cSDimitry Andric hash_code DataCode = hash_value(Data); 416*349cc55cSDimitry Andric Code = hash_combine(Code, DataCode); 417*349cc55cSDimitry Andric } 418*349cc55cSDimitry Andric hash_code Code; 419*349cc55cSDimitry Andric }; 420*349cc55cSDimitry Andric 421*349cc55cSDimitry Andric using HashCodeHashBuilder = HashBuilder<hashbuilder_detail::HashCodeHasher, 422*349cc55cSDimitry Andric support::endianness::native>; 423*349cc55cSDimitry Andric } // namespace hashbuilder_detail 424*349cc55cSDimitry Andric 425*349cc55cSDimitry Andric /// Provide a default implementation of `hash_value` when `addHash(const T &)` 426*349cc55cSDimitry Andric /// is supported. 427*349cc55cSDimitry Andric template <typename T> 428*349cc55cSDimitry Andric std::enable_if_t< 429*349cc55cSDimitry Andric is_detected<hashbuilder_detail::HashCodeHashBuilder::HasAddHashT, T>::value, 430*349cc55cSDimitry Andric hash_code> 431*349cc55cSDimitry Andric hash_value(const T &Value) { 432*349cc55cSDimitry Andric hashbuilder_detail::HashCodeHashBuilder HBuilder; 433*349cc55cSDimitry Andric HBuilder.add(Value); 434*349cc55cSDimitry Andric return HBuilder.getHasher().Code; 435*349cc55cSDimitry Andric } 436*349cc55cSDimitry Andric } // end namespace llvm 437*349cc55cSDimitry Andric 438*349cc55cSDimitry Andric #endif // LLVM_SUPPORT_HASHBUILDER_H 439