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> 26349cc55cSDimitry Andric #include <utility> 27349cc55cSDimitry Andric 28349cc55cSDimitry Andric namespace llvm { 29349cc55cSDimitry Andric 30349cc55cSDimitry Andric namespace hashbuilder_detail { 31349cc55cSDimitry Andric /// Trait to indicate whether a type's bits can be hashed directly (after 32349cc55cSDimitry Andric /// endianness correction). 33349cc55cSDimitry Andric template <typename U> 34349cc55cSDimitry Andric struct IsHashableData 35349cc55cSDimitry Andric : std::integral_constant<bool, is_integral_or_enum<U>::value> {}; 36349cc55cSDimitry Andric 37349cc55cSDimitry Andric } // namespace hashbuilder_detail 38349cc55cSDimitry Andric 39349cc55cSDimitry Andric /// Declares the hasher member, and functions forwarding directly to the hasher. 40349cc55cSDimitry Andric template <typename HasherT> class HashBuilderBase { 41349cc55cSDimitry Andric public: 42*81ad6265SDimitry Andric template <typename HasherT_ = HasherT> 43*81ad6265SDimitry Andric using HashResultTy = decltype(std::declval<HasherT_ &>().final()); 44*81ad6265SDimitry Andric 45349cc55cSDimitry Andric HasherT &getHasher() { return Hasher; } 46349cc55cSDimitry Andric 47349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 48349cc55cSDimitry Andric /// 49349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 50349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 51349cc55cSDimitry Andric /// contraints. 52349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { this->getHasher().update(Data); } 53349cc55cSDimitry Andric 54349cc55cSDimitry Andric /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. 55349cc55cSDimitry Andric /// 56349cc55cSDimitry Andric /// This may not take the size of `Data` into account. 57349cc55cSDimitry Andric /// Users of this function should pay attention to respect endianness 58349cc55cSDimitry Andric /// contraints. 59349cc55cSDimitry Andric void update(StringRef Data) { 60349cc55cSDimitry Andric update(makeArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), 61349cc55cSDimitry Andric Data.size())); 62349cc55cSDimitry Andric } 63349cc55cSDimitry Andric 64349cc55cSDimitry Andric /// Forward to `HasherT::final()` if available. 65*81ad6265SDimitry Andric template <typename HasherT_ = HasherT> HashResultTy<HasherT_> final() { 66349cc55cSDimitry Andric return this->getHasher().final(); 67349cc55cSDimitry Andric } 68349cc55cSDimitry Andric 69349cc55cSDimitry Andric /// Forward to `HasherT::result()` if available. 70*81ad6265SDimitry Andric template <typename HasherT_ = HasherT> HashResultTy<HasherT_> result() { 71349cc55cSDimitry Andric return this->getHasher().result(); 72349cc55cSDimitry Andric } 73349cc55cSDimitry Andric 74349cc55cSDimitry Andric protected: 75349cc55cSDimitry Andric explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {} 76349cc55cSDimitry Andric 77349cc55cSDimitry Andric template <typename... ArgTypes> 78349cc55cSDimitry Andric explicit HashBuilderBase(ArgTypes &&...Args) 79349cc55cSDimitry Andric : OptionalHasher(in_place, std::forward<ArgTypes>(Args)...), 80349cc55cSDimitry Andric Hasher(*OptionalHasher) {} 81349cc55cSDimitry Andric 82349cc55cSDimitry Andric private: 83349cc55cSDimitry Andric Optional<HasherT> OptionalHasher; 84349cc55cSDimitry Andric HasherT &Hasher; 85349cc55cSDimitry Andric }; 86349cc55cSDimitry Andric 87349cc55cSDimitry Andric /// Implementation of the `HashBuilder` interface. 88349cc55cSDimitry Andric /// 89349cc55cSDimitry Andric /// `support::endianness::native` is not supported. `HashBuilder` is 90349cc55cSDimitry Andric /// expected to canonicalize `support::endianness::native` to one of 91349cc55cSDimitry Andric /// `support::endianness::big` or `support::endianness::little`. 92349cc55cSDimitry Andric template <typename HasherT, support::endianness Endianness> 93349cc55cSDimitry Andric class HashBuilderImpl : public HashBuilderBase<HasherT> { 94349cc55cSDimitry Andric static_assert(Endianness != support::endianness::native, 95349cc55cSDimitry Andric "HashBuilder should canonicalize endianness"); 96349cc55cSDimitry Andric 97349cc55cSDimitry Andric public: 98349cc55cSDimitry Andric explicit HashBuilderImpl(HasherT &Hasher) 99349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Hasher) {} 100349cc55cSDimitry Andric template <typename... ArgTypes> 101349cc55cSDimitry Andric explicit HashBuilderImpl(ArgTypes &&...Args) 102349cc55cSDimitry Andric : HashBuilderBase<HasherT>(Args...) {} 103349cc55cSDimitry Andric 104349cc55cSDimitry Andric /// Implement hashing for hashable data types, e.g. integral or enum values. 105349cc55cSDimitry Andric template <typename T> 106349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, 107349cc55cSDimitry Andric HashBuilderImpl &> 108349cc55cSDimitry Andric add(T Value) { 109349cc55cSDimitry Andric return adjustForEndiannessAndAdd(Value); 110349cc55cSDimitry Andric } 111349cc55cSDimitry Andric 112349cc55cSDimitry Andric /// Support hashing `ArrayRef`. 113349cc55cSDimitry Andric /// 114349cc55cSDimitry Andric /// `Value.size()` is taken into account to ensure cases like 115349cc55cSDimitry Andric /// ``` 116349cc55cSDimitry Andric /// builder.add({1}); 117349cc55cSDimitry Andric /// builder.add({2, 3}); 118349cc55cSDimitry Andric /// ``` 119349cc55cSDimitry Andric /// and 120349cc55cSDimitry Andric /// ``` 121349cc55cSDimitry Andric /// builder.add({1, 2}); 122349cc55cSDimitry Andric /// builder.add({3}); 123349cc55cSDimitry Andric /// ``` 124349cc55cSDimitry Andric /// do not collide. 125349cc55cSDimitry Andric template <typename T> HashBuilderImpl &add(ArrayRef<T> Value) { 126349cc55cSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go 127349cc55cSDimitry Andric // through the `update` fast path. But that would rely on the implementation 128349cc55cSDimitry Andric // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call 129349cc55cSDimitry Andric // `update` to guarantee the fast path. 130349cc55cSDimitry Andric add(Value.size()); 131349cc55cSDimitry Andric if (hashbuilder_detail::IsHashableData<T>::value && 132349cc55cSDimitry Andric Endianness == support::endian::system_endianness()) { 133349cc55cSDimitry Andric this->update( 134349cc55cSDimitry Andric makeArrayRef(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()); 162349cc55cSDimitry Andric this->update(makeArrayRef(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()) { 212349cc55cSDimitry Andric /// HBuilder.update(makeArrayRef( 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()) { 242349cc55cSDimitry Andric /// HBuilder.update(makeArrayRef( 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) { 264349cc55cSDimitry Andric add(Value.first); 265349cc55cSDimitry Andric add(Value.second); 266349cc55cSDimitry Andric return *this; 267349cc55cSDimitry Andric } 268349cc55cSDimitry Andric 269349cc55cSDimitry Andric template <typename... Ts> HashBuilderImpl &add(const std::tuple<Ts...> &Arg) { 270349cc55cSDimitry Andric return addTupleHelper(Arg, typename std::index_sequence_for<Ts...>()); 271349cc55cSDimitry Andric } 272349cc55cSDimitry Andric 273349cc55cSDimitry Andric /// A convenenience variadic helper. 274349cc55cSDimitry Andric /// It simply iterates over its arguments, in order. 275349cc55cSDimitry Andric /// ``` 276349cc55cSDimitry Andric /// add(Arg1, Arg2); 277349cc55cSDimitry Andric /// ``` 278349cc55cSDimitry Andric /// is equivalent to 279349cc55cSDimitry Andric /// ``` 280349cc55cSDimitry Andric /// add(Arg1) 281349cc55cSDimitry Andric /// add(Arg2) 282349cc55cSDimitry Andric /// ``` 283349cc55cSDimitry Andric template <typename T, typename... Ts> 284349cc55cSDimitry Andric typename std::enable_if<(sizeof...(Ts) >= 1), HashBuilderImpl &>::type 285349cc55cSDimitry Andric add(const T &FirstArg, const Ts &...Args) { 286349cc55cSDimitry Andric add(FirstArg); 287349cc55cSDimitry Andric add(Args...); 288349cc55cSDimitry Andric return *this; 289349cc55cSDimitry Andric } 290349cc55cSDimitry Andric 291349cc55cSDimitry Andric template <typename ForwardIteratorT> 292349cc55cSDimitry Andric HashBuilderImpl &addRange(ForwardIteratorT First, ForwardIteratorT Last) { 293349cc55cSDimitry Andric add(std::distance(First, Last)); 294349cc55cSDimitry Andric return addRangeElements(First, Last); 295349cc55cSDimitry Andric } 296349cc55cSDimitry Andric 297349cc55cSDimitry Andric template <typename RangeT> HashBuilderImpl &addRange(const RangeT &Range) { 298349cc55cSDimitry Andric return addRange(adl_begin(Range), adl_end(Range)); 299349cc55cSDimitry Andric } 300349cc55cSDimitry Andric 301349cc55cSDimitry Andric template <typename ForwardIteratorT> 302349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(ForwardIteratorT First, 303349cc55cSDimitry Andric ForwardIteratorT Last) { 304349cc55cSDimitry Andric return addRangeElementsImpl( 305349cc55cSDimitry Andric First, Last, 306349cc55cSDimitry Andric typename std::iterator_traits<ForwardIteratorT>::iterator_category()); 307349cc55cSDimitry Andric } 308349cc55cSDimitry Andric 309349cc55cSDimitry Andric template <typename RangeT> 310349cc55cSDimitry Andric HashBuilderImpl &addRangeElements(const RangeT &Range) { 311349cc55cSDimitry Andric return addRangeElements(adl_begin(Range), adl_end(Range)); 312349cc55cSDimitry Andric } 313349cc55cSDimitry Andric 314349cc55cSDimitry Andric template <typename T> 315349cc55cSDimitry Andric using HasByteSwapT = decltype(support::endian::byte_swap( 316349cc55cSDimitry Andric std::declval<T &>(), support::endianness::little)); 317349cc55cSDimitry Andric /// Adjust `Value` for the target endianness and add it to the hash. 318349cc55cSDimitry Andric template <typename T> 319349cc55cSDimitry Andric std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilderImpl &> 320349cc55cSDimitry Andric adjustForEndiannessAndAdd(const T &Value) { 321349cc55cSDimitry Andric T SwappedValue = support::endian::byte_swap(Value, Endianness); 322349cc55cSDimitry Andric this->update(makeArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue), 323349cc55cSDimitry Andric sizeof(SwappedValue))); 324349cc55cSDimitry Andric return *this; 325349cc55cSDimitry Andric } 326349cc55cSDimitry Andric 327349cc55cSDimitry Andric private: 328349cc55cSDimitry Andric template <typename... Ts, std::size_t... Indices> 329349cc55cSDimitry Andric HashBuilderImpl &addTupleHelper(const std::tuple<Ts...> &Arg, 330349cc55cSDimitry Andric std::index_sequence<Indices...>) { 331349cc55cSDimitry Andric add(std::get<Indices>(Arg)...); 332349cc55cSDimitry Andric return *this; 333349cc55cSDimitry Andric } 334349cc55cSDimitry Andric 335349cc55cSDimitry Andric // FIXME: Once available, specialize this function for `contiguous_iterator`s, 336349cc55cSDimitry Andric // and use it for `ArrayRef` and `StringRef`. 337349cc55cSDimitry Andric template <typename ForwardIteratorT> 338349cc55cSDimitry Andric HashBuilderImpl &addRangeElementsImpl(ForwardIteratorT First, 339349cc55cSDimitry Andric ForwardIteratorT Last, 340349cc55cSDimitry Andric std::forward_iterator_tag) { 341349cc55cSDimitry Andric for (auto It = First; It != Last; ++It) 342349cc55cSDimitry Andric add(*It); 343349cc55cSDimitry Andric return *this; 344349cc55cSDimitry Andric } 345349cc55cSDimitry Andric 346349cc55cSDimitry Andric template <typename T> 347349cc55cSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value && 348349cc55cSDimitry Andric Endianness == support::endian::system_endianness(), 349349cc55cSDimitry Andric HashBuilderImpl &> 350349cc55cSDimitry Andric addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) { 351349cc55cSDimitry Andric this->update(makeArrayRef(reinterpret_cast<const uint8_t *>(First), 352349cc55cSDimitry Andric (Last - First) * sizeof(T))); 353349cc55cSDimitry Andric return *this; 354349cc55cSDimitry Andric } 355349cc55cSDimitry Andric }; 356349cc55cSDimitry Andric 357349cc55cSDimitry Andric /// Interface to help hash various types through a hasher type. 358349cc55cSDimitry Andric /// 359349cc55cSDimitry Andric /// Via provided specializations of `add`, `addRange`, and `addRangeElements` 360349cc55cSDimitry Andric /// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed 361349cc55cSDimitry Andric /// without requiring any knowledge of hashed types from the hasher type. 362349cc55cSDimitry Andric /// 363349cc55cSDimitry Andric /// The only method expected from the templated hasher type `HasherT` is: 364349cc55cSDimitry Andric /// * void update(ArrayRef<uint8_t> Data) 365349cc55cSDimitry Andric /// 366349cc55cSDimitry Andric /// Additionally, the following methods will be forwarded to the hasher type: 367349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().final()) final() 368349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().result()) result() 369349cc55cSDimitry Andric /// 370349cc55cSDimitry Andric /// From a user point of view, the interface provides the following: 371349cc55cSDimitry Andric /// * `template<typename T> add(const T &Value)` 372349cc55cSDimitry Andric /// The `add` function implements hashing of various types. 373349cc55cSDimitry Andric /// * `template <typename ItT> void addRange(ItT First, ItT Last)` 374349cc55cSDimitry Andric /// The `addRange` function is designed to aid hashing a range of values. 375349cc55cSDimitry Andric /// It explicitly adds the size of the range in the hash. 376349cc55cSDimitry Andric /// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)` 377349cc55cSDimitry Andric /// The `addRangeElements` function is also designed to aid hashing a range of 378349cc55cSDimitry Andric /// values. In contrast to `addRange`, it **ignores** the size of the range, 379349cc55cSDimitry Andric /// behaving as if elements were added one at a time with `add`. 380349cc55cSDimitry Andric /// 381349cc55cSDimitry Andric /// User-defined `struct` types can participate in this interface by providing 382349cc55cSDimitry Andric /// an `addHash` templated function. See the associated template specialization 383349cc55cSDimitry Andric /// for details. 384349cc55cSDimitry Andric /// 385349cc55cSDimitry Andric /// This interface does not impose requirements on the hasher 386349cc55cSDimitry Andric /// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for 387349cc55cSDimitry Andric /// variable-size types; for example for 388349cc55cSDimitry Andric /// ``` 389349cc55cSDimitry Andric /// builder.add({1}); 390349cc55cSDimitry Andric /// builder.add({2, 3}); 391349cc55cSDimitry Andric /// ``` 392349cc55cSDimitry Andric /// and 393349cc55cSDimitry Andric /// ``` 394349cc55cSDimitry Andric /// builder.add({1, 2}); 395349cc55cSDimitry Andric /// builder.add({3}); 396349cc55cSDimitry Andric /// ``` 397349cc55cSDimitry Andric /// . Thus, specializations of `add` and `addHash` for variable-size types must 398349cc55cSDimitry Andric /// not assume that the hasher type considers the size as part of the hash; they 399349cc55cSDimitry Andric /// must explicitly add the size to the hash. See for example specializations 400349cc55cSDimitry Andric /// for `ArrayRef` and `StringRef`. 401349cc55cSDimitry Andric /// 402349cc55cSDimitry Andric /// Additionally, since types are eventually forwarded to the hasher's 403349cc55cSDimitry Andric /// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash 404349cc55cSDimitry Andric /// computation (for example when computing `add((int)123)`). 405349cc55cSDimitry Andric /// Specifiying a non-`native` `Endianness` template parameter allows to compute 406349cc55cSDimitry Andric /// stable hash across platforms with different endianness. 407349cc55cSDimitry Andric template <class HasherT, support::endianness Endianness> 408349cc55cSDimitry Andric using HashBuilder = 409349cc55cSDimitry Andric HashBuilderImpl<HasherT, (Endianness == support::endianness::native 410349cc55cSDimitry Andric ? support::endian::system_endianness() 411349cc55cSDimitry Andric : Endianness)>; 412349cc55cSDimitry Andric 413349cc55cSDimitry Andric namespace hashbuilder_detail { 414349cc55cSDimitry Andric class HashCodeHasher { 415349cc55cSDimitry Andric public: 416349cc55cSDimitry Andric HashCodeHasher() : Code(0) {} 417349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) { 418349cc55cSDimitry Andric hash_code DataCode = hash_value(Data); 419349cc55cSDimitry Andric Code = hash_combine(Code, DataCode); 420349cc55cSDimitry Andric } 421349cc55cSDimitry Andric hash_code Code; 422349cc55cSDimitry Andric }; 423349cc55cSDimitry Andric 424349cc55cSDimitry Andric using HashCodeHashBuilder = HashBuilder<hashbuilder_detail::HashCodeHasher, 425349cc55cSDimitry Andric support::endianness::native>; 426349cc55cSDimitry Andric } // namespace hashbuilder_detail 427349cc55cSDimitry Andric 428349cc55cSDimitry Andric /// Provide a default implementation of `hash_value` when `addHash(const T &)` 429349cc55cSDimitry Andric /// is supported. 430349cc55cSDimitry Andric template <typename T> 431349cc55cSDimitry Andric std::enable_if_t< 432349cc55cSDimitry Andric is_detected<hashbuilder_detail::HashCodeHashBuilder::HasAddHashT, T>::value, 433349cc55cSDimitry Andric hash_code> 434349cc55cSDimitry Andric hash_value(const T &Value) { 435349cc55cSDimitry Andric hashbuilder_detail::HashCodeHashBuilder HBuilder; 436349cc55cSDimitry Andric HBuilder.add(Value); 437349cc55cSDimitry Andric return HBuilder.getHasher().Code; 438349cc55cSDimitry Andric } 439349cc55cSDimitry Andric } // end namespace llvm 440349cc55cSDimitry Andric 441349cc55cSDimitry Andric #endif // LLVM_SUPPORT_HASHBUILDER_H 442