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