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>
26bdd1243dSDimitry 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
getHasher()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.
update(ArrayRef<uint8_t> Data)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.
update(StringRef Data)60349cc55cSDimitry Andric void update(StringRef Data) {
61bdd1243dSDimitry Andric update(
62bdd1243dSDimitry Andric ArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), Data.size()));
63349cc55cSDimitry Andric }
64349cc55cSDimitry Andric
65349cc55cSDimitry Andric /// Forward to `HasherT::final()` if available.
final()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.
result()7181ad6265SDimitry Andric template <typename HasherT_ = HasherT> HashResultTy<HasherT_> result() {
72349cc55cSDimitry Andric return this->getHasher().result();
73349cc55cSDimitry Andric }
74349cc55cSDimitry Andric
75349cc55cSDimitry Andric protected:
HashBuilderBase(HasherT & Hasher)76349cc55cSDimitry Andric explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {}
77349cc55cSDimitry Andric
78349cc55cSDimitry Andric template <typename... ArgTypes>
HashBuilderBase(ArgTypes &&...Args)79349cc55cSDimitry Andric explicit HashBuilderBase(ArgTypes &&...Args)
80bdd1243dSDimitry Andric : OptionalHasher(std::in_place, std::forward<ArgTypes>(Args)...),
81349cc55cSDimitry Andric Hasher(*OptionalHasher) {}
82349cc55cSDimitry Andric
83349cc55cSDimitry Andric private:
84bdd1243dSDimitry Andric std::optional<HasherT> OptionalHasher;
85349cc55cSDimitry Andric HasherT &Hasher;
86349cc55cSDimitry Andric };
87349cc55cSDimitry Andric
88349cc55cSDimitry Andric /// Interface to help hash various types through a hasher type.
89349cc55cSDimitry Andric ///
90349cc55cSDimitry Andric /// Via provided specializations of `add`, `addRange`, and `addRangeElements`
91349cc55cSDimitry Andric /// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed
92349cc55cSDimitry Andric /// without requiring any knowledge of hashed types from the hasher type.
93349cc55cSDimitry Andric ///
94349cc55cSDimitry Andric /// The only method expected from the templated hasher type `HasherT` is:
95349cc55cSDimitry Andric /// * void update(ArrayRef<uint8_t> Data)
96349cc55cSDimitry Andric ///
97349cc55cSDimitry Andric /// Additionally, the following methods will be forwarded to the hasher type:
98349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().final()) final()
99349cc55cSDimitry Andric /// * decltype(std::declval<HasherT &>().result()) result()
100349cc55cSDimitry Andric ///
101349cc55cSDimitry Andric /// From a user point of view, the interface provides the following:
102349cc55cSDimitry Andric /// * `template<typename T> add(const T &Value)`
103349cc55cSDimitry Andric /// The `add` function implements hashing of various types.
104349cc55cSDimitry Andric /// * `template <typename ItT> void addRange(ItT First, ItT Last)`
105349cc55cSDimitry Andric /// The `addRange` function is designed to aid hashing a range of values.
106349cc55cSDimitry Andric /// It explicitly adds the size of the range in the hash.
107349cc55cSDimitry Andric /// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)`
108349cc55cSDimitry Andric /// The `addRangeElements` function is also designed to aid hashing a range of
109349cc55cSDimitry Andric /// values. In contrast to `addRange`, it **ignores** the size of the range,
110349cc55cSDimitry Andric /// behaving as if elements were added one at a time with `add`.
111349cc55cSDimitry Andric ///
112349cc55cSDimitry Andric /// User-defined `struct` types can participate in this interface by providing
113349cc55cSDimitry Andric /// an `addHash` templated function. See the associated template specialization
114349cc55cSDimitry Andric /// for details.
115349cc55cSDimitry Andric ///
116349cc55cSDimitry Andric /// This interface does not impose requirements on the hasher
117349cc55cSDimitry Andric /// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for
118349cc55cSDimitry Andric /// variable-size types; for example for
119349cc55cSDimitry Andric /// ```
120349cc55cSDimitry Andric /// builder.add({1});
121349cc55cSDimitry Andric /// builder.add({2, 3});
122349cc55cSDimitry Andric /// ```
123349cc55cSDimitry Andric /// and
124349cc55cSDimitry Andric /// ```
125349cc55cSDimitry Andric /// builder.add({1, 2});
126349cc55cSDimitry Andric /// builder.add({3});
127349cc55cSDimitry Andric /// ```
128349cc55cSDimitry Andric /// . Thus, specializations of `add` and `addHash` for variable-size types must
129349cc55cSDimitry Andric /// not assume that the hasher type considers the size as part of the hash; they
130349cc55cSDimitry Andric /// must explicitly add the size to the hash. See for example specializations
131349cc55cSDimitry Andric /// for `ArrayRef` and `StringRef`.
132349cc55cSDimitry Andric ///
133349cc55cSDimitry Andric /// Additionally, since types are eventually forwarded to the hasher's
134349cc55cSDimitry Andric /// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash
135349cc55cSDimitry Andric /// computation (for example when computing `add((int)123)`).
136349cc55cSDimitry Andric /// Specifiying a non-`native` `Endianness` template parameter allows to compute
137349cc55cSDimitry Andric /// stable hash across platforms with different endianness.
138*5f757f3fSDimitry Andric template <typename HasherT, llvm::endianness Endianness>
139*5f757f3fSDimitry Andric class HashBuilder : public HashBuilderBase<HasherT> {
140*5f757f3fSDimitry Andric public:
HashBuilder(HasherT & Hasher)141*5f757f3fSDimitry Andric explicit HashBuilder(HasherT &Hasher) : HashBuilderBase<HasherT>(Hasher) {}
142*5f757f3fSDimitry Andric template <typename... ArgTypes>
HashBuilder(ArgTypes &&...Args)143*5f757f3fSDimitry Andric explicit HashBuilder(ArgTypes &&...Args)
144*5f757f3fSDimitry Andric : HashBuilderBase<HasherT>(Args...) {}
145*5f757f3fSDimitry Andric
146*5f757f3fSDimitry Andric /// Implement hashing for hashable data types, e.g. integral or enum values.
147*5f757f3fSDimitry Andric template <typename T>
148*5f757f3fSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, HashBuilder &>
add(T Value)149*5f757f3fSDimitry Andric add(T Value) {
150*5f757f3fSDimitry Andric return adjustForEndiannessAndAdd(Value);
151*5f757f3fSDimitry Andric }
152*5f757f3fSDimitry Andric
153*5f757f3fSDimitry Andric /// Support hashing `ArrayRef`.
154*5f757f3fSDimitry Andric ///
155*5f757f3fSDimitry Andric /// `Value.size()` is taken into account to ensure cases like
156*5f757f3fSDimitry Andric /// ```
157*5f757f3fSDimitry Andric /// builder.add({1});
158*5f757f3fSDimitry Andric /// builder.add({2, 3});
159*5f757f3fSDimitry Andric /// ```
160*5f757f3fSDimitry Andric /// and
161*5f757f3fSDimitry Andric /// ```
162*5f757f3fSDimitry Andric /// builder.add({1, 2});
163*5f757f3fSDimitry Andric /// builder.add({3});
164*5f757f3fSDimitry Andric /// ```
165*5f757f3fSDimitry Andric /// do not collide.
add(ArrayRef<T> Value)166*5f757f3fSDimitry Andric template <typename T> HashBuilder &add(ArrayRef<T> Value) {
167*5f757f3fSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go
168*5f757f3fSDimitry Andric // through the `update` fast path. But that would rely on the implementation
169*5f757f3fSDimitry Andric // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call
170*5f757f3fSDimitry Andric // `update` to guarantee the fast path.
171*5f757f3fSDimitry Andric add(Value.size());
172*5f757f3fSDimitry Andric if (hashbuilder_detail::IsHashableData<T>::value &&
173*5f757f3fSDimitry Andric Endianness == llvm::endianness::native) {
174*5f757f3fSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()),
175*5f757f3fSDimitry Andric Value.size() * sizeof(T)));
176*5f757f3fSDimitry Andric } else {
177*5f757f3fSDimitry Andric for (auto &V : Value)
178*5f757f3fSDimitry Andric add(V);
179*5f757f3fSDimitry Andric }
180*5f757f3fSDimitry Andric return *this;
181*5f757f3fSDimitry Andric }
182*5f757f3fSDimitry Andric
183*5f757f3fSDimitry Andric /// Support hashing `StringRef`.
184*5f757f3fSDimitry Andric ///
185*5f757f3fSDimitry Andric /// `Value.size()` is taken into account to ensure cases like
186*5f757f3fSDimitry Andric /// ```
187*5f757f3fSDimitry Andric /// builder.add("a");
188*5f757f3fSDimitry Andric /// builder.add("bc");
189*5f757f3fSDimitry Andric /// ```
190*5f757f3fSDimitry Andric /// and
191*5f757f3fSDimitry Andric /// ```
192*5f757f3fSDimitry Andric /// builder.add("ab");
193*5f757f3fSDimitry Andric /// builder.add("c");
194*5f757f3fSDimitry Andric /// ```
195*5f757f3fSDimitry Andric /// do not collide.
add(StringRef Value)196*5f757f3fSDimitry Andric HashBuilder &add(StringRef Value) {
197*5f757f3fSDimitry Andric // As of implementation time, simply calling `addRange(Value)` would also go
198*5f757f3fSDimitry Andric // through `update`. But that would rely on the implementation of
199*5f757f3fSDimitry Andric // `StringRef::begin()` and `StringRef::end()`. Explicitly call `update` to
200*5f757f3fSDimitry Andric // guarantee the fast path.
201*5f757f3fSDimitry Andric add(Value.size());
202*5f757f3fSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()),
203*5f757f3fSDimitry Andric Value.size()));
204*5f757f3fSDimitry Andric return *this;
205*5f757f3fSDimitry Andric }
206*5f757f3fSDimitry Andric
207*5f757f3fSDimitry Andric template <typename T>
208*5f757f3fSDimitry Andric using HasAddHashT =
209*5f757f3fSDimitry Andric decltype(addHash(std::declval<HashBuilder &>(), std::declval<T &>()));
210*5f757f3fSDimitry Andric /// Implement hashing for user-defined `struct`s.
211*5f757f3fSDimitry Andric ///
212*5f757f3fSDimitry Andric /// Any user-define `struct` can participate in hashing via `HashBuilder` by
213*5f757f3fSDimitry Andric /// providing a `addHash` templated function.
214*5f757f3fSDimitry Andric ///
215*5f757f3fSDimitry Andric /// ```
216*5f757f3fSDimitry Andric /// template <typename HasherT, llvm::endianness Endianness>
217*5f757f3fSDimitry Andric /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
218*5f757f3fSDimitry Andric /// const UserDefinedStruct &Value);
219*5f757f3fSDimitry Andric /// ```
220*5f757f3fSDimitry Andric ///
221*5f757f3fSDimitry Andric /// For example:
222*5f757f3fSDimitry Andric /// ```
223*5f757f3fSDimitry Andric /// struct SimpleStruct {
224*5f757f3fSDimitry Andric /// char c;
225*5f757f3fSDimitry Andric /// int i;
226*5f757f3fSDimitry Andric /// };
227*5f757f3fSDimitry Andric ///
228*5f757f3fSDimitry Andric /// template <typename HasherT, llvm::endianness Endianness>
229*5f757f3fSDimitry Andric /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
230*5f757f3fSDimitry Andric /// const SimpleStruct &Value) {
231*5f757f3fSDimitry Andric /// HBuilder.add(Value.c);
232*5f757f3fSDimitry Andric /// HBuilder.add(Value.i);
233*5f757f3fSDimitry Andric /// }
234*5f757f3fSDimitry Andric /// ```
235*5f757f3fSDimitry Andric ///
236*5f757f3fSDimitry Andric /// To avoid endianness issues, specializations of `addHash` should
237*5f757f3fSDimitry Andric /// generally rely on exising `add`, `addRange`, and `addRangeElements`
238*5f757f3fSDimitry Andric /// functions. If directly using `update`, an implementation must correctly
239*5f757f3fSDimitry Andric /// handle endianness.
240*5f757f3fSDimitry Andric ///
241*5f757f3fSDimitry Andric /// ```
242*5f757f3fSDimitry Andric /// struct __attribute__ ((packed)) StructWithFastHash {
243*5f757f3fSDimitry Andric /// int I;
244*5f757f3fSDimitry Andric /// char C;
245*5f757f3fSDimitry Andric ///
246*5f757f3fSDimitry Andric /// // If possible, we want to hash both `I` and `C` in a single
247*5f757f3fSDimitry Andric /// // `update` call for performance concerns.
248*5f757f3fSDimitry Andric /// template <typename HasherT, llvm::endianness Endianness>
249*5f757f3fSDimitry Andric /// friend void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
250*5f757f3fSDimitry Andric /// const StructWithFastHash &Value) {
251*5f757f3fSDimitry Andric /// if (Endianness == llvm::endianness::native) {
252*5f757f3fSDimitry Andric /// HBuilder.update(ArrayRef(
253*5f757f3fSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value), sizeof(Value)));
254*5f757f3fSDimitry Andric /// } else {
255*5f757f3fSDimitry Andric /// // Rely on existing `add` methods to handle endianness.
256*5f757f3fSDimitry Andric /// HBuilder.add(Value.I);
257*5f757f3fSDimitry Andric /// HBuilder.add(Value.C);
258*5f757f3fSDimitry Andric /// }
259*5f757f3fSDimitry Andric /// }
260*5f757f3fSDimitry Andric /// };
261*5f757f3fSDimitry Andric /// ```
262*5f757f3fSDimitry Andric ///
263*5f757f3fSDimitry Andric /// To avoid collisions, specialization of `addHash` for variable-size
264*5f757f3fSDimitry Andric /// types must take the size into account.
265*5f757f3fSDimitry Andric ///
266*5f757f3fSDimitry Andric /// For example:
267*5f757f3fSDimitry Andric /// ```
268*5f757f3fSDimitry Andric /// struct CustomContainer {
269*5f757f3fSDimitry Andric /// private:
270*5f757f3fSDimitry Andric /// size_t Size;
271*5f757f3fSDimitry Andric /// int Elements[100];
272*5f757f3fSDimitry Andric ///
273*5f757f3fSDimitry Andric /// public:
274*5f757f3fSDimitry Andric /// CustomContainer(size_t Size) : Size(Size) {
275*5f757f3fSDimitry Andric /// for (size_t I = 0; I != Size; ++I)
276*5f757f3fSDimitry Andric /// Elements[I] = I;
277*5f757f3fSDimitry Andric /// }
278*5f757f3fSDimitry Andric /// template <typename HasherT, llvm::endianness Endianness>
279*5f757f3fSDimitry Andric /// friend void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
280*5f757f3fSDimitry Andric /// const CustomContainer &Value) {
281*5f757f3fSDimitry Andric /// if (Endianness == llvm::endianness::native) {
282*5f757f3fSDimitry Andric /// HBuilder.update(ArrayRef(
283*5f757f3fSDimitry Andric /// reinterpret_cast<const uint8_t *>(&Value.Size),
284*5f757f3fSDimitry Andric /// sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0])));
285*5f757f3fSDimitry Andric /// } else {
286*5f757f3fSDimitry Andric /// // `addRange` will take care of encoding the size.
287*5f757f3fSDimitry Andric /// HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] +
288*5f757f3fSDimitry Andric /// Value.Size);
289*5f757f3fSDimitry Andric /// }
290*5f757f3fSDimitry Andric /// }
291*5f757f3fSDimitry Andric /// };
292*5f757f3fSDimitry Andric /// ```
293*5f757f3fSDimitry Andric template <typename T>
294*5f757f3fSDimitry Andric std::enable_if_t<is_detected<HasAddHashT, T>::value &&
295*5f757f3fSDimitry Andric !hashbuilder_detail::IsHashableData<T>::value,
296*5f757f3fSDimitry Andric HashBuilder &>
add(const T & Value)297*5f757f3fSDimitry Andric add(const T &Value) {
298*5f757f3fSDimitry Andric addHash(*this, Value);
299*5f757f3fSDimitry Andric return *this;
300*5f757f3fSDimitry Andric }
301*5f757f3fSDimitry Andric
302*5f757f3fSDimitry Andric template <typename T1, typename T2>
add(const std::pair<T1,T2> & Value)303*5f757f3fSDimitry Andric HashBuilder &add(const std::pair<T1, T2> &Value) {
304*5f757f3fSDimitry Andric return add(Value.first, Value.second);
305*5f757f3fSDimitry Andric }
306*5f757f3fSDimitry Andric
add(const std::tuple<Ts...> & Arg)307*5f757f3fSDimitry Andric template <typename... Ts> HashBuilder &add(const std::tuple<Ts...> &Arg) {
308*5f757f3fSDimitry Andric std::apply([this](const auto &...Args) { this->add(Args...); }, Arg);
309*5f757f3fSDimitry Andric return *this;
310*5f757f3fSDimitry Andric }
311*5f757f3fSDimitry Andric
312*5f757f3fSDimitry Andric /// A convenenience variadic helper.
313*5f757f3fSDimitry Andric /// It simply iterates over its arguments, in order.
314*5f757f3fSDimitry Andric /// ```
315*5f757f3fSDimitry Andric /// add(Arg1, Arg2);
316*5f757f3fSDimitry Andric /// ```
317*5f757f3fSDimitry Andric /// is equivalent to
318*5f757f3fSDimitry Andric /// ```
319*5f757f3fSDimitry Andric /// add(Arg1)
320*5f757f3fSDimitry Andric /// add(Arg2)
321*5f757f3fSDimitry Andric /// ```
322*5f757f3fSDimitry Andric template <typename... Ts>
add(const Ts &...Args)323*5f757f3fSDimitry Andric std::enable_if_t<(sizeof...(Ts) > 1), HashBuilder &> add(const Ts &...Args) {
324*5f757f3fSDimitry Andric return (add(Args), ...);
325*5f757f3fSDimitry Andric }
326*5f757f3fSDimitry Andric
327*5f757f3fSDimitry Andric template <typename ForwardIteratorT>
addRange(ForwardIteratorT First,ForwardIteratorT Last)328*5f757f3fSDimitry Andric HashBuilder &addRange(ForwardIteratorT First, ForwardIteratorT Last) {
329*5f757f3fSDimitry Andric add(std::distance(First, Last));
330*5f757f3fSDimitry Andric return addRangeElements(First, Last);
331*5f757f3fSDimitry Andric }
332*5f757f3fSDimitry Andric
addRange(const RangeT & Range)333*5f757f3fSDimitry Andric template <typename RangeT> HashBuilder &addRange(const RangeT &Range) {
334*5f757f3fSDimitry Andric return addRange(adl_begin(Range), adl_end(Range));
335*5f757f3fSDimitry Andric }
336*5f757f3fSDimitry Andric
337*5f757f3fSDimitry Andric template <typename ForwardIteratorT>
addRangeElements(ForwardIteratorT First,ForwardIteratorT Last)338*5f757f3fSDimitry Andric HashBuilder &addRangeElements(ForwardIteratorT First, ForwardIteratorT Last) {
339*5f757f3fSDimitry Andric return addRangeElementsImpl(
340*5f757f3fSDimitry Andric First, Last,
341*5f757f3fSDimitry Andric typename std::iterator_traits<ForwardIteratorT>::iterator_category());
342*5f757f3fSDimitry Andric }
343*5f757f3fSDimitry Andric
344*5f757f3fSDimitry Andric template <typename RangeT>
addRangeElements(const RangeT & Range)345*5f757f3fSDimitry Andric HashBuilder &addRangeElements(const RangeT &Range) {
346*5f757f3fSDimitry Andric return addRangeElements(adl_begin(Range), adl_end(Range));
347*5f757f3fSDimitry Andric }
348*5f757f3fSDimitry Andric
349*5f757f3fSDimitry Andric template <typename T>
350*5f757f3fSDimitry Andric using HasByteSwapT = decltype(support::endian::byte_swap(
351*5f757f3fSDimitry Andric std::declval<T &>(), llvm::endianness::little));
352*5f757f3fSDimitry Andric /// Adjust `Value` for the target endianness and add it to the hash.
353*5f757f3fSDimitry Andric template <typename T>
354*5f757f3fSDimitry Andric std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilder &>
adjustForEndiannessAndAdd(const T & Value)355*5f757f3fSDimitry Andric adjustForEndiannessAndAdd(const T &Value) {
356*5f757f3fSDimitry Andric T SwappedValue = support::endian::byte_swap(Value, Endianness);
357*5f757f3fSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue),
358*5f757f3fSDimitry Andric sizeof(SwappedValue)));
359*5f757f3fSDimitry Andric return *this;
360*5f757f3fSDimitry Andric }
361*5f757f3fSDimitry Andric
362*5f757f3fSDimitry Andric private:
363*5f757f3fSDimitry Andric // FIXME: Once available, specialize this function for `contiguous_iterator`s,
364*5f757f3fSDimitry Andric // and use it for `ArrayRef` and `StringRef`.
365*5f757f3fSDimitry Andric template <typename ForwardIteratorT>
addRangeElementsImpl(ForwardIteratorT First,ForwardIteratorT Last,std::forward_iterator_tag)366*5f757f3fSDimitry Andric HashBuilder &addRangeElementsImpl(ForwardIteratorT First,
367*5f757f3fSDimitry Andric ForwardIteratorT Last,
368*5f757f3fSDimitry Andric std::forward_iterator_tag) {
369*5f757f3fSDimitry Andric for (auto It = First; It != Last; ++It)
370*5f757f3fSDimitry Andric add(*It);
371*5f757f3fSDimitry Andric return *this;
372*5f757f3fSDimitry Andric }
373*5f757f3fSDimitry Andric
374*5f757f3fSDimitry Andric template <typename T>
375*5f757f3fSDimitry Andric std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value &&
376*5f757f3fSDimitry Andric Endianness == llvm::endianness::native,
377*5f757f3fSDimitry Andric HashBuilder &>
addRangeElementsImpl(T * First,T * Last,std::forward_iterator_tag)378*5f757f3fSDimitry Andric addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) {
379*5f757f3fSDimitry Andric this->update(ArrayRef(reinterpret_cast<const uint8_t *>(First),
380*5f757f3fSDimitry Andric (Last - First) * sizeof(T)));
381*5f757f3fSDimitry Andric return *this;
382*5f757f3fSDimitry Andric }
383*5f757f3fSDimitry Andric };
384349cc55cSDimitry Andric
385349cc55cSDimitry Andric namespace hashbuilder_detail {
386349cc55cSDimitry Andric class HashCodeHasher {
387349cc55cSDimitry Andric public:
HashCodeHasher()388349cc55cSDimitry Andric HashCodeHasher() : Code(0) {}
update(ArrayRef<uint8_t> Data)389349cc55cSDimitry Andric void update(ArrayRef<uint8_t> Data) {
390349cc55cSDimitry Andric hash_code DataCode = hash_value(Data);
391349cc55cSDimitry Andric Code = hash_combine(Code, DataCode);
392349cc55cSDimitry Andric }
393349cc55cSDimitry Andric hash_code Code;
394349cc55cSDimitry Andric };
395349cc55cSDimitry Andric
396*5f757f3fSDimitry Andric using HashCodeHashBuilder =
397*5f757f3fSDimitry Andric HashBuilder<hashbuilder_detail::HashCodeHasher, llvm::endianness::native>;
398349cc55cSDimitry Andric } // namespace hashbuilder_detail
399349cc55cSDimitry Andric
400349cc55cSDimitry Andric /// Provide a default implementation of `hash_value` when `addHash(const T &)`
401349cc55cSDimitry Andric /// is supported.
402349cc55cSDimitry Andric template <typename T>
403349cc55cSDimitry Andric std::enable_if_t<
404349cc55cSDimitry Andric is_detected<hashbuilder_detail::HashCodeHashBuilder::HasAddHashT, T>::value,
405349cc55cSDimitry Andric hash_code>
hash_value(const T & Value)406349cc55cSDimitry Andric hash_value(const T &Value) {
407349cc55cSDimitry Andric hashbuilder_detail::HashCodeHashBuilder HBuilder;
408349cc55cSDimitry Andric HBuilder.add(Value);
409349cc55cSDimitry Andric return HBuilder.getHasher().Code;
410349cc55cSDimitry Andric }
411349cc55cSDimitry Andric } // end namespace llvm
412349cc55cSDimitry Andric
413349cc55cSDimitry Andric #endif // LLVM_SUPPORT_HASHBUILDER_H
414