1349cc55cSDimitry Andric //===- llvm/Support/HashBuilder.h - Convenient hashing interface-*- C++ -*-===// 2349cc55cSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6349cc55cSDimitry Andric // 7349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8349cc55cSDimitry Andric // 9349cc55cSDimitry Andric // This file implements an interface allowing to conveniently build hashes of 10349cc55cSDimitry Andric // various data types, without relying on the underlying hasher type to know 11349cc55cSDimitry Andric // about hashed data types. 12349cc55cSDimitry Andric // 13349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 14349cc55cSDimitry Andric 15349cc55cSDimitry Andric #ifndef LLVM_SUPPORT_HASHBUILDER_H 16349cc55cSDimitry Andric #define LLVM_SUPPORT_HASHBUILDER_H 17349cc55cSDimitry Andric 18349cc55cSDimitry Andric #include "llvm/ADT/ArrayRef.h" 19349cc55cSDimitry Andric #include "llvm/ADT/Hashing.h" 20349cc55cSDimitry Andric #include "llvm/ADT/STLExtras.h" 21349cc55cSDimitry Andric #include "llvm/ADT/StringRef.h" 22349cc55cSDimitry Andric #include "llvm/Support/Endian.h" 23349cc55cSDimitry Andric #include "llvm/Support/type_traits.h" 24349cc55cSDimitry Andric 25349cc55cSDimitry Andric #include <iterator> 26*bdd1243dSDimitry Andric #include <optional> 27349cc55cSDimitry Andric #include <utility> 28349cc55cSDimitry Andric 29349cc55cSDimitry Andric namespace llvm { 30349cc55cSDimitry Andric 31349cc55cSDimitry Andric namespace hashbuilder_detail { 32349cc55cSDimitry Andric /// Trait to indicate whether a type's bits can be hashed directly (after 33349cc55cSDimitry Andric /// endianness correction). 34349cc55cSDimitry Andric template <typename U> 35349cc55cSDimitry Andric struct IsHashableData 36349cc55cSDimitry Andric : std::integral_constant<bool, is_integral_or_enum<U>::value> {}; 37349cc55cSDimitry Andric 38349cc55cSDimitry Andric } // namespace hashbuilder_detail 39349cc55cSDimitry Andric 40349cc55cSDimitry Andric /// Declares the hasher member, and functions forwarding directly to the hasher. 41349cc55cSDimitry Andric template <typename HasherT> class HashBuilderBase { 42349cc55cSDimitry Andric public: 4381ad6265SDimitry Andric template <typename HasherT_ = HasherT> 4481ad6265SDimitry Andric using HashResultTy = decltype(std::declval<HasherT_ &>().final()); 4581ad6265SDimitry Andric 46349cc55cSDimitry Andric HasherT &getHasher() { return Hasher; } 47349cc55cSDimitry Andric 48349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 49349cc55cSDimitry Andric /// 50349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 51349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 52349cc55cSDimitry Andric /// contraints. 53349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { this->getHasher().update(Data); } 54349cc55cSDimitry Andric 55349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 56349cc55cSDimitry Andric /// 57349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 58349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 59349cc55cSDimitry Andric /// contraints. 60349cc55cSDimitry Andric void update(StringRef Data) { 61*bdd1243dSDimitry Andric update( 62*bdd1243dSDimitry Andric ArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), Data.size())); 63349cc55cSDimitry Andric } 64349cc55cSDimitry Andric 65349cc55cSDimitry Andric /// Forward to `HasherT::final()` if available. 6681ad6265SDimitry Andric template <typename HasherT_ = HasherT> HashResultTy<HasherT_> final() { 67349cc55cSDimitry Andric return this->getHasher().final(); 68349cc55cSDimitry Andric } 69349cc55cSDimitry Andric 70349cc55cSDimitry Andric /// Forward to `HasherT::result()` if available. 7181ad6265SDimitry Andric template <typename HasherT_ = HasherT> HashResultTy<HasherT_> result() { 72349cc55cSDimitry Andric return this->getHasher().result(); 73349cc55cSDimitry Andric } 74349cc55cSDimitry Andric 75349cc55cSDimitry Andric protected: 76349cc55cSDimitry Andric explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {} 77349cc55cSDimitry Andric 78349cc55cSDimitry Andric template <typename... ArgTypes> 79349cc55cSDimitry Andric explicit HashBuilderBase(ArgTypes &&...Args) 80*bdd1243dSDimitry Andric : OptionalHasher(std::in_place, std::forward<ArgTypes>(Args)...), 81349cc55cSDimitry Andric Hasher(*OptionalHasher) {} 82349cc55cSDimitry Andric 83349cc55cSDimitry Andric private: 84*bdd1243dSDimitry Andric std::optional<HasherT> OptionalHasher; 85349cc55cSDimitry Andric HasherT &Hasher; 86349cc55cSDimitry Andric }; 87349cc55cSDimitry Andric 88349cc55cSDimitry Andric /// Implementation of the `HashBuilder` interface. 89349cc55cSDimitry Andric /// 90349cc55cSDimitry Andric /// `support::endianness::native` is not supported. `HashBuilder` is 91349cc55cSDimitry Andric /// expected to canonicalize `support::endianness::native` to one of 92349cc55cSDimitry Andric /// `support::endianness::big` or `support::endianness::little`. 93349cc55cSDimitry Andric template <typename HasherT, support::endianness Endianness> 94349cc55cSDimitry Andric class HashBuilderImpl : public HashBuilderBase<HasherT> { 95349cc55cSDimitry Andric static_assert(Endianness != support::endianness::native, 96349cc55cSDimitry Andric "HashBuilder should canonicalize endianness"); 97349cc55cSDimitry Andric 98349cc55cSDimitry Andric public: 99349cc55cSDimitry Andric explicit HashBuilderImpl(HasherT &Hasher) 100349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Hasher) {} 101349cc55cSDimitry Andric template <typename... ArgTypes> 102349cc55cSDimitry Andric explicit HashBuilderImpl(ArgTypes &&...Args) 103349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Args...) {} 104349cc55cSDimitry Andric 105349cc55cSDimitry Andric /// Implement hashing for hashable data types, e.g. integral or enum values. 106349cc55cSDimitry Andric template <typename T> 107349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, 108349cc55cSDimitry Andric HashBuilderImpl &> 109349cc55cSDimitry Andric add(T Value) { 110349cc55cSDimitry Andric return adjustForEndiannessAndAdd(Value); 111349cc55cSDimitry Andric } 112349cc55cSDimitry Andric 113349cc55cSDimitry Andric /// Support hashing `ArrayRef`. 114349cc55cSDimitry Andric /// 115349cc55cSDimitry Andric /// `Value.size()` is taken into account to ensure cases like 116349cc55cSDimitry Andric /// ``` 117349cc55cSDimitry Andric /// builder.add({1}); 118349cc55cSDimitry Andric /// builder.add({2, 3}); 119349cc55cSDimitry Andric /// ``` 120349cc55cSDimitry Andric /// and 121349cc55cSDimitry Andric /// ``` 122349cc55cSDimitry Andric /// builder.add({1, 2}); 123349cc55cSDimitry Andric /// builder.add({3}); 124349cc55cSDimitry Andric /// ``` 125349cc55cSDimitry Andric /// do not collide. 126349cc55cSDimitry Andric template <typename T> HashBuilderImpl &add(ArrayRef<T> Value) { 127349cc55cSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go 128349cc55cSDimitry Andric // through the `update` fast path. But that would rely on the implementation 129349cc55cSDimitry Andric // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call 130349cc55cSDimitry Andric // `update` to guarantee the fast path. 131349cc55cSDimitry Andric add(Value.size()); 132349cc55cSDimitry Andric if (hashbuilder_detail::IsHashableData<T>::value && 133349cc55cSDimitry Andric Endianness == support::endian::system_endianness()) { 134*bdd1243dSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), 135349cc55cSDimitry Andric Value.size() * sizeof(T))); 136349cc55cSDimitry Andric } else { 137349cc55cSDimitry Andric for (auto &V : Value) 138349cc55cSDimitry Andric add(V); 139349cc55cSDimitry Andric } 140349cc55cSDimitry Andric return *this; 141349cc55cSDimitry Andric } 142349cc55cSDimitry Andric 143349cc55cSDimitry Andric /// Support hashing `StringRef`. 144349cc55cSDimitry Andric /// 145349cc55cSDimitry Andric /// `Value.size()` is taken into account to ensure cases like 146349cc55cSDimitry Andric /// ``` 147349cc55cSDimitry Andric /// builder.add("a"); 148349cc55cSDimitry Andric /// builder.add("bc"); 149349cc55cSDimitry Andric /// ``` 150349cc55cSDimitry Andric /// and 151349cc55cSDimitry Andric /// ``` 152349cc55cSDimitry Andric /// builder.add("ab"); 153349cc55cSDimitry Andric /// builder.add("c"); 154349cc55cSDimitry Andric /// ``` 155349cc55cSDimitry Andric /// do not collide. 156349cc55cSDimitry Andric HashBuilderImpl &add(StringRef Value) { 157349cc55cSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go 158349cc55cSDimitry Andric // through `update`. But that would rely on the implementation of 159349cc55cSDimitry Andric // `StringRef::begin()` and `StringRef::end()`. Explicitly call `update` to 160349cc55cSDimitry Andric // guarantee the fast path. 161349cc55cSDimitry Andric add(Value.size()); 162*bdd1243dSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), 163349cc55cSDimitry Andric Value.size())); 164349cc55cSDimitry Andric return *this; 165349cc55cSDimitry Andric } 166349cc55cSDimitry Andric 167349cc55cSDimitry Andric template <typename T> 168349cc55cSDimitry Andric using HasAddHashT = 169349cc55cSDimitry Andric decltype(addHash(std::declval<HashBuilderImpl &>(), std::declval<T &>())); 170349cc55cSDimitry Andric /// Implement hashing for user-defined `struct`s. 171349cc55cSDimitry Andric /// 172349cc55cSDimitry Andric /// Any user-define `struct` can participate in hashing via `HashBuilder` by 173349cc55cSDimitry Andric /// providing a `addHash` templated function. 174349cc55cSDimitry Andric /// 175349cc55cSDimitry Andric /// ``` 176349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 177349cc55cSDimitry Andric /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder, 178349cc55cSDimitry Andric /// const UserDefinedStruct &Value); 179349cc55cSDimitry Andric /// ``` 180349cc55cSDimitry Andric /// 181349cc55cSDimitry Andric /// For example: 182349cc55cSDimitry Andric /// ``` 183349cc55cSDimitry Andric /// struct SimpleStruct { 184349cc55cSDimitry Andric /// char c; 185349cc55cSDimitry Andric /// int i; 186349cc55cSDimitry Andric /// }; 187349cc55cSDimitry Andric /// 188349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 189349cc55cSDimitry Andric /// void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 190349cc55cSDimitry Andric /// const SimpleStruct &Value) { 191349cc55cSDimitry Andric /// HBuilder.add(Value.c); 192349cc55cSDimitry Andric /// HBuilder.add(Value.i); 193349cc55cSDimitry Andric /// } 194349cc55cSDimitry Andric /// ``` 195349cc55cSDimitry Andric /// 196349cc55cSDimitry Andric /// To avoid endianness issues, specializations of `addHash` should 197349cc55cSDimitry Andric /// generally rely on exising `add`, `addRange`, and `addRangeElements` 198349cc55cSDimitry Andric /// functions. If directly using `update`, an implementation must correctly 199349cc55cSDimitry Andric /// handle endianness. 200349cc55cSDimitry Andric /// 201349cc55cSDimitry Andric /// ``` 202349cc55cSDimitry Andric /// struct __attribute__ ((packed)) StructWithFastHash { 203349cc55cSDimitry Andric /// int I; 204349cc55cSDimitry Andric /// char C; 205349cc55cSDimitry Andric /// 206349cc55cSDimitry Andric /// // If possible, we want to hash both `I` and `C` in a single 207349cc55cSDimitry Andric /// // `update` call for performance concerns. 208349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 209349cc55cSDimitry Andric /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 210349cc55cSDimitry Andric /// const StructWithFastHash &Value) { 211349cc55cSDimitry Andric /// if (Endianness == support::endian::system_endianness()) { 212*bdd1243dSDimitry Andric /// HBuilder.update(ArrayRef( 213349cc55cSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value), sizeof(Value))); 214349cc55cSDimitry Andric /// } else { 215349cc55cSDimitry Andric /// // Rely on existing `add` methods to handle endianness. 216349cc55cSDimitry Andric /// HBuilder.add(Value.I); 217349cc55cSDimitry Andric /// HBuilder.add(Value.C); 218349cc55cSDimitry Andric /// } 219349cc55cSDimitry Andric /// } 220349cc55cSDimitry Andric /// }; 221349cc55cSDimitry Andric /// ``` 222349cc55cSDimitry Andric /// 223349cc55cSDimitry Andric /// To avoid collisions, specialization of `addHash` for variable-size 224349cc55cSDimitry Andric /// types must take the size into account. 225349cc55cSDimitry Andric /// 226349cc55cSDimitry Andric /// For example: 227349cc55cSDimitry Andric /// ``` 228349cc55cSDimitry Andric /// struct CustomContainer { 229349cc55cSDimitry Andric /// private: 230349cc55cSDimitry Andric /// size_t Size; 231349cc55cSDimitry Andric /// int Elements[100]; 232349cc55cSDimitry Andric /// 233349cc55cSDimitry Andric /// public: 234349cc55cSDimitry Andric /// CustomContainer(size_t Size) : Size(Size) { 235349cc55cSDimitry Andric /// for (size_t I = 0; I != Size; ++I) 236349cc55cSDimitry Andric /// Elements[I] = I; 237349cc55cSDimitry Andric /// } 238349cc55cSDimitry Andric /// template <typename HasherT, support::endianness Endianness> 239349cc55cSDimitry Andric /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, 240349cc55cSDimitry Andric /// const CustomContainer &Value) { 241349cc55cSDimitry Andric /// if (Endianness == support::endian::system_endianness()) { 242*bdd1243dSDimitry Andric /// HBuilder.update(ArrayRef( 243349cc55cSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value.Size), 244349cc55cSDimitry Andric /// sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0]))); 245349cc55cSDimitry Andric /// } else { 246349cc55cSDimitry Andric /// // `addRange` will take care of encoding the size. 247349cc55cSDimitry Andric /// HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + 248349cc55cSDimitry Andric /// Value.Size); 249349cc55cSDimitry Andric /// } 250349cc55cSDimitry Andric /// } 251349cc55cSDimitry Andric /// }; 252349cc55cSDimitry Andric /// ``` 253349cc55cSDimitry Andric template <typename T> 254349cc55cSDimitry Andric std::enable_if_t<is_detected<HasAddHashT, T>::value && 255349cc55cSDimitry Andric !hashbuilder_detail::IsHashableData<T>::value, 256349cc55cSDimitry Andric HashBuilderImpl &> 257349cc55cSDimitry Andric add(const T &Value) { 258349cc55cSDimitry Andric addHash(*this, Value); 259349cc55cSDimitry Andric return *this; 260349cc55cSDimitry Andric } 261349cc55cSDimitry Andric 262349cc55cSDimitry Andric template <typename T1, typename T2> 263349cc55cSDimitry Andric HashBuilderImpl &add(const std::pair<T1, T2> &Value) { 264*bdd1243dSDimitry Andric return add(Value.first, Value.second); 265349cc55cSDimitry Andric } 266349cc55cSDimitry Andric 267349cc55cSDimitry Andric template <typename... Ts> HashBuilderImpl &add(const std::tuple<Ts...> &Arg) { 268*bdd1243dSDimitry Andric std::apply([this](const auto &...Args) { this->add(Args...); }, Arg); 269*bdd1243dSDimitry Andric return *this; 270349cc55cSDimitry Andric } 271349cc55cSDimitry Andric 272349cc55cSDimitry Andric /// A convenenience variadic helper. 273349cc55cSDimitry Andric /// It simply iterates over its arguments, in order. 274349cc55cSDimitry Andric /// ``` 275349cc55cSDimitry Andric /// add(Arg1, Arg2); 276349cc55cSDimitry Andric /// ``` 277349cc55cSDimitry Andric /// is equivalent to 278349cc55cSDimitry Andric /// ``` 279349cc55cSDimitry Andric /// add(Arg1) 280349cc55cSDimitry Andric /// add(Arg2) 281349cc55cSDimitry Andric /// ``` 282*bdd1243dSDimitry Andric template <typename... Ts> 283*bdd1243dSDimitry Andric std::enable_if_t<(sizeof...(Ts) > 1), HashBuilderImpl &> 284*bdd1243dSDimitry Andric add(const Ts &...Args) { 285*bdd1243dSDimitry Andric return (add(Args), ...); 286349cc55cSDimitry Andric } 287349cc55cSDimitry Andric 288349cc55cSDimitry Andric template <typename ForwardIteratorT> 289349cc55cSDimitry Andric HashBuilderImpl &addRange(ForwardIteratorT First, ForwardIteratorT Last) { 290349cc55cSDimitry Andric add(std::distance(First, Last)); 291349cc55cSDimitry Andric return addRangeElements(First, Last); 292349cc55cSDimitry Andric } 293349cc55cSDimitry Andric 294349cc55cSDimitry Andric template <typename RangeT> HashBuilderImpl &addRange(const RangeT &Range) { 295349cc55cSDimitry Andric return addRange(adl_begin(Range), adl_end(Range)); 296349cc55cSDimitry Andric } 297349cc55cSDimitry Andric 298349cc55cSDimitry Andric template <typename ForwardIteratorT> 299349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(ForwardIteratorT First, 300349cc55cSDimitry Andric ForwardIteratorT Last) { 301349cc55cSDimitry Andric return addRangeElementsImpl( 302349cc55cSDimitry Andric First, Last, 303349cc55cSDimitry Andric typename std::iterator_traits<ForwardIteratorT>::iterator_category()); 304349cc55cSDimitry Andric } 305349cc55cSDimitry Andric 306349cc55cSDimitry Andric template <typename RangeT> 307349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(const RangeT &Range) { 308349cc55cSDimitry Andric return addRangeElements(adl_begin(Range), adl_end(Range)); 309349cc55cSDimitry Andric } 310349cc55cSDimitry Andric 311349cc55cSDimitry Andric template <typename T> 312349cc55cSDimitry Andric using HasByteSwapT = decltype(support::endian::byte_swap( 313349cc55cSDimitry Andric std::declval<T &>(), support::endianness::little)); 314349cc55cSDimitry Andric /// Adjust `Value` for the target endianness and add it to the hash. 315349cc55cSDimitry Andric template <typename T> 316349cc55cSDimitry Andric std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilderImpl &> 317349cc55cSDimitry Andric adjustForEndiannessAndAdd(const T &Value) { 318349cc55cSDimitry Andric T SwappedValue = support::endian::byte_swap(Value, Endianness); 319*bdd1243dSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue), 320349cc55cSDimitry Andric sizeof(SwappedValue))); 321349cc55cSDimitry Andric return *this; 322349cc55cSDimitry Andric } 323349cc55cSDimitry Andric 324349cc55cSDimitry Andric private: 325349cc55cSDimitry Andric // FIXME: Once available, specialize this function for `contiguous_iterator`s, 326349cc55cSDimitry Andric // and use it for `ArrayRef` and `StringRef`. 327349cc55cSDimitry Andric template <typename ForwardIteratorT> 328349cc55cSDimitry Andric HashBuilderImpl &addRangeElementsImpl(ForwardIteratorT First, 329349cc55cSDimitry Andric ForwardIteratorT Last, 330349cc55cSDimitry Andric std::forward_iterator_tag) { 331349cc55cSDimitry Andric for (auto It = First; It != Last; ++It) 332349cc55cSDimitry Andric add(*It); 333349cc55cSDimitry Andric return *this; 334349cc55cSDimitry Andric } 335349cc55cSDimitry Andric 336349cc55cSDimitry Andric template <typename T> 337349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value && 338349cc55cSDimitry Andric Endianness == support::endian::system_endianness(), 339349cc55cSDimitry Andric HashBuilderImpl &> 340349cc55cSDimitry Andric addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) { 341*bdd1243dSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(First), 342349cc55cSDimitry Andric (Last - First) * sizeof(T))); 343349cc55cSDimitry Andric return *this; 344349cc55cSDimitry Andric } 345349cc55cSDimitry Andric }; 346349cc55cSDimitry Andric 347349cc55cSDimitry Andric /// Interface to help hash various types through a hasher type. 348349cc55cSDimitry Andric /// 349349cc55cSDimitry Andric /// Via provided specializations of `add`, `addRange`, and `addRangeElements` 350349cc55cSDimitry Andric /// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed 351349cc55cSDimitry Andric /// without requiring any knowledge of hashed types from the hasher type. 352349cc55cSDimitry Andric /// 353349cc55cSDimitry Andric /// The only method expected from the templated hasher type `HasherT` is: 354349cc55cSDimitry Andric /// * void update(ArrayRef<uint8_t> Data) 355349cc55cSDimitry Andric /// 356349cc55cSDimitry Andric /// Additionally, the following methods will be forwarded to the hasher type: 357349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().final()) final() 358349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().result()) result() 359349cc55cSDimitry Andric /// 360349cc55cSDimitry Andric /// From a user point of view, the interface provides the following: 361349cc55cSDimitry Andric /// * `template<typename T> add(const T &Value)` 362349cc55cSDimitry Andric /// The `add` function implements hashing of various types. 363349cc55cSDimitry Andric /// * `template <typename ItT> void addRange(ItT First, ItT Last)` 364349cc55cSDimitry Andric /// The `addRange` function is designed to aid hashing a range of values. 365349cc55cSDimitry Andric /// It explicitly adds the size of the range in the hash. 366349cc55cSDimitry Andric /// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)` 367349cc55cSDimitry Andric /// The `addRangeElements` function is also designed to aid hashing a range of 368349cc55cSDimitry Andric /// values. In contrast to `addRange`, it **ignores** the size of the range, 369349cc55cSDimitry Andric /// behaving as if elements were added one at a time with `add`. 370349cc55cSDimitry Andric /// 371349cc55cSDimitry Andric /// User-defined `struct` types can participate in this interface by providing 372349cc55cSDimitry Andric /// an `addHash` templated function. See the associated template specialization 373349cc55cSDimitry Andric /// for details. 374349cc55cSDimitry Andric /// 375349cc55cSDimitry Andric /// This interface does not impose requirements on the hasher 376349cc55cSDimitry Andric /// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for 377349cc55cSDimitry Andric /// variable-size types; for example for 378349cc55cSDimitry Andric /// ``` 379349cc55cSDimitry Andric /// builder.add({1}); 380349cc55cSDimitry Andric /// builder.add({2, 3}); 381349cc55cSDimitry Andric /// ``` 382349cc55cSDimitry Andric /// and 383349cc55cSDimitry Andric /// ``` 384349cc55cSDimitry Andric /// builder.add({1, 2}); 385349cc55cSDimitry Andric /// builder.add({3}); 386349cc55cSDimitry Andric /// ``` 387349cc55cSDimitry Andric /// . Thus, specializations of `add` and `addHash` for variable-size types must 388349cc55cSDimitry Andric /// not assume that the hasher type considers the size as part of the hash; they 389349cc55cSDimitry Andric /// must explicitly add the size to the hash. See for example specializations 390349cc55cSDimitry Andric /// for `ArrayRef` and `StringRef`. 391349cc55cSDimitry Andric /// 392349cc55cSDimitry Andric /// Additionally, since types are eventually forwarded to the hasher's 393349cc55cSDimitry Andric /// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash 394349cc55cSDimitry Andric /// computation (for example when computing `add((int)123)`). 395349cc55cSDimitry Andric /// Specifiying a non-`native` `Endianness` template parameter allows to compute 396349cc55cSDimitry Andric /// stable hash across platforms with different endianness. 397349cc55cSDimitry Andric template <class HasherT, support::endianness Endianness> 398349cc55cSDimitry Andric using HashBuilder = 399349cc55cSDimitry Andric HashBuilderImpl<HasherT, (Endianness == support::endianness::native 400349cc55cSDimitry Andric ? support::endian::system_endianness() 401349cc55cSDimitry Andric : Endianness)>; 402349cc55cSDimitry Andric 403349cc55cSDimitry Andric namespace hashbuilder_detail { 404349cc55cSDimitry Andric class HashCodeHasher { 405349cc55cSDimitry Andric public: 406349cc55cSDimitry Andric HashCodeHasher() : Code(0) {} 407349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { 408349cc55cSDimitry Andric hash_code DataCode = hash_value(Data); 409349cc55cSDimitry Andric Code = hash_combine(Code, DataCode); 410349cc55cSDimitry Andric } 411349cc55cSDimitry Andric hash_code Code; 412349cc55cSDimitry Andric }; 413349cc55cSDimitry Andric 414349cc55cSDimitry Andric using HashCodeHashBuilder = HashBuilder<hashbuilder_detail::HashCodeHasher, 415349cc55cSDimitry Andric support::endianness::native>; 416349cc55cSDimitry Andric } // namespace hashbuilder_detail 417349cc55cSDimitry Andric 418349cc55cSDimitry Andric /// Provide a default implementation of `hash_value` when `addHash(const T &)` 419349cc55cSDimitry Andric /// is supported. 420349cc55cSDimitry Andric template <typename T> 421349cc55cSDimitry Andric std::enable_if_t< 422349cc55cSDimitry Andric is_detected<hashbuilder_detail::HashCodeHashBuilder::HasAddHashT, T>::value, 423349cc55cSDimitry Andric hash_code> 424349cc55cSDimitry Andric hash_value(const T &Value) { 425349cc55cSDimitry Andric hashbuilder_detail::HashCodeHashBuilder HBuilder; 426349cc55cSDimitry Andric HBuilder.add(Value); 427349cc55cSDimitry Andric return HBuilder.getHasher().Code; 428349cc55cSDimitry Andric } 429349cc55cSDimitry Andric } // end namespace llvm 430349cc55cSDimitry Andric 431349cc55cSDimitry Andric #endif // LLVM_SUPPORT_HASHBUILDER_H 432