xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/Support/HashBuilder.h (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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