Clingo
Loading...
Searching...
No Matches
hash.hh
1#pragma once
2
3#include <clingo/util/immutable_array.hh>
4#include <clingo/util/immutable_value.hh>
5#include <clingo/util/small_vector.hh>
6
7#include <cstring>
8#include <memory>
9#include <numeric>
10#include <optional>
11#include <span>
12#include <typeinfo>
13#include <variant>
14#include <vector>
15
16namespace CppClingo::Util {
17
20
22inline auto hash_mix(size_t h) -> size_t;
23
25template <class... T> inline auto hash_combine(T... a) -> size_t;
26
28auto value_hash(std::type_info const &x) -> size_t;
29
31template <class T> auto value_hash(T const &x) -> size_t;
32
34template <class T> auto value_hash(T *x) -> size_t;
35
37template <class T> auto value_hash(std::optional<T> const &x) -> size_t;
38
40template <class T> auto value_hash(std::reference_wrapper<T> const &x) -> size_t;
41
43template <class T, class D> auto value_hash(std::unique_ptr<T, D> const &x) -> size_t;
44
46template <class T> auto value_hash(immutable_value<T> const &x) -> size_t;
47
49template <class T, class U> auto value_hash(std::pair<T, U> const &x) -> size_t;
50
52template <class... T> auto value_hash(std::tuple<T...> const &x) -> size_t;
53
55template <class... T> auto value_hash(std::variant<T...> const &x) -> size_t;
56
58template <class T, size_t E> auto value_hash(std::span<T, E> const &x) -> size_t;
59
61template <class T, class A> auto value_hash(std::vector<T, A> const &x) -> size_t;
62
64template <class T> auto value_hash(Util::immutable_array<T> const &x) -> size_t;
65
67template <class T, size_t N> auto value_hash(Util::small_vector<T, N> const &x) -> size_t;
68
70auto value_hash(char const *x) -> size_t;
71
73auto value_hash(std::string_view const &x) -> size_t;
74
76auto value_hash(std::string const &x) -> size_t;
77
79template <class T> auto value_hash_range(T const &x) -> size_t;
80
82template <class T, class... Args> auto value_hash_record(Args const &...x) -> size_t;
83
87 template <class T> auto operator()(T const &x) const -> size_t { return value_hash(x); }
88};
89
93 using is_transparent = void;
94
96 template <class T, class U> auto operator()(T const &a, U const &b) const -> bool { return a == b; }
98 auto operator()(char const *a, char const *b) const -> bool { return std::strcmp(a, b) == 0; }
100 template <class T, class U>
101 auto operator()(std::reference_wrapper<T> const &a, std::reference_wrapper<U> const &b) const -> bool {
102 return operator()(a.get(), b.get());
103 }
105 template <class T, class U> auto operator()(std::optional<T> const &a, std::optional<U> const &b) const -> bool {
106 return (!a && !b) || (a && b && operator()(*a, *b));
107 }
109 template <class T, class U> auto operator()(T *a, U *b) const -> bool {
110 return a == b || (a != nullptr && b != nullptr && operator()(*a, *b));
111 }
113 template <class T, class D, class U, class E>
114 auto operator()(std::unique_ptr<T, D> const &a, std::unique_ptr<U, E> const &b) const -> bool {
115 return operator()(a.get(), b.get());
116 }
118 template <class T, class U, class V, class W>
119 auto operator()(std::pair<T, U> const &a, std::pair<V, W> const &b) const -> bool {
120 return operator()(a.first, b.first) && operator()(a.second, b.second);
121 }
123 template <class... T, class... U>
124 auto operator()(std::tuple<T...> const &a, std::tuple<U...> const &b) const -> bool {
125 static_assert(sizeof...(T) == sizeof...(U));
126 return [&, this]<size_t... I>([[maybe_unused]] std::index_sequence<I...> seq) {
127 return (this->operator()(std::get<I>(a), std::get<I>(b)) && ...);
128 }(std::index_sequence_for<T...>());
129 }
131 template <class... T, class... U>
132 auto operator()(std::variant<T...> const &a, std::variant<U...> const &b) const -> bool {
133 static_assert(sizeof...(T) == sizeof...(U));
134 auto i = a.index();
135 return i == b.index() && [&, this]<size_t... I>([[maybe_unused]] std::index_sequence<I...> seq) {
136 return ((i == I && this->operator()(std::get<I>(a), std::get<I>(b))) || ...);
137 }(std::index_sequence_for<T...>());
138 }
140 template <class T, size_t E, class U, size_t F>
141 auto operator()(std::span<T, E> const &a, std::span<U, F> const &b) const -> bool {
142 return std::equal(a.begin(), a.end(), b.begin(), b.end(), *this);
143 }
145 template <class T, class A, class U, class B>
146 auto operator()(std::vector<T, A> const &a, std::vector<U, B> const &b) const -> bool {
147 return std::equal(a.begin(), a.end(), b.begin(), b.end(), *this);
148 }
149};
150
155 public:
157 array_hash(size_t size) : size_{size} {}
159 template <class T> auto operator()(T const *sym) const -> size_t { return value_hash(std::span(sym, size_)); }
160
161 private:
162 size_t size_;
163};
164
169 public:
171 array_equal_to(size_t size) : size_{size} {}
173 template <class T> auto operator()(T const *a, T const *b) const -> bool {
174 return value_equal_to{}(std::span(a, size_), std::span(b, size_));
175 }
176
177 private:
178 size_t size_;
179};
180
181// Implementation of value_hash
182
183namespace Detail {
184
185inline auto hash_combine() -> size_t {
186 return 0;
187}
188
189inline auto hash_combine(size_t a) -> size_t {
190 return a;
191}
192
193inline auto hash_combine(size_t seed, size_t h) -> size_t {
194 // NOLINTBEGIN(readability-magic-numbers)
195 if constexpr (sizeof(size_t) == sizeof(uint64_t)) {
196 seed *= 0x87c37b91114253d5;
197 seed = (seed >> 31) | (seed << 33);
198 seed *= 0x4cf5ad432745937f;
199 seed ^= h;
200 seed = (seed >> 27) | (seed << 37);
201 seed = seed * 5 + 0x52dce729;
202 } else {
203 seed *= 0xcc9e2d51;
204 seed = (seed >> 17) | (seed << 15);
205 seed *= 0x1b873593;
206 seed ^= h;
207 seed = (seed >> 19) | (seed << 13);
208 seed = seed * 5 + 0xe6546b64;
209 }
210 return seed;
211 // NOLINTEND(readability-magic-numbers)
212}
213
214template <class... T> inline auto hash_combine(size_t a, size_t b, T... c) -> size_t {
215 return hash_combine(hash_combine(a, b), c...);
216}
217
218} // namespace Detail
219
220inline auto hash_mix(size_t h) -> size_t {
221 // NOLINTBEGIN(readability-magic-numbers)
222 if constexpr (sizeof(size_t) == sizeof(uint64_t)) {
223 h ^= h >> 33;
224 h *= 0xff51afd7ed558ccd;
225 h ^= h >> 33;
226 h *= 0xc4ceb9fe1a85ec53;
227 h ^= h >> 33;
228 } else {
229 h ^= h >> 16;
230 h *= 0x85ebca6b;
231 h ^= h >> 13;
232 h *= 0xc2b2ae35;
233 h ^= h >> 16;
234 }
235 return h;
236 // NOLINTEND(readability-magic-numbers)
237}
238
239template <class... T> inline auto hash_combine(T... a) -> size_t {
240 return Detail::hash_combine(a...);
241}
242
243template <class T> auto value_hash_range(T const &x) -> size_t {
244 // NOLINTBEGIN(readability-magic-numbers)
245 // note: we unroll the loop for small sizes, this compiles very well and leads to noticable speedups
246 // length 8 should cover typical sizes for argument tuples etc. in clingo
247 auto small = []<std::size_t... n>(std::index_sequence<n...>, auto const &x) {
248 return Util::hash_combine(Util::value_hash(x[n])...);
249 };
250 switch (x.size()) {
251 case 0:
252 return small(std::make_index_sequence<0>(), x);
253 case 1:
254 return small(std::make_index_sequence<1>(), x);
255 case 2:
256 return small(std::make_index_sequence<2>(), x);
257 case 3:
258 return small(std::make_index_sequence<3>(), x);
259 case 4:
260 return small(std::make_index_sequence<4>(), x);
261 case 5:
262 return small(std::make_index_sequence<5>(), x);
263 case 6:
264 return small(std::make_index_sequence<6>(), x);
265 case 7:
266 return small(std::make_index_sequence<7>(), x);
267 case 8:
268 return small(std::make_index_sequence<8>(), x);
269 default:
270 return std::accumulate(
271 x.begin() + 1, x.end(), Util::value_hash(*x.begin()),
272 [](auto const &seed, auto const &x) { return Util::hash_combine(seed, Util::value_hash(x)); });
273 }
274 // NOLINTEND(readability-magic-numbers)
275}
276
277inline auto value_hash(std::type_info const &x) -> size_t {
278 return hash_mix(x.hash_code());
279}
280
281template <class T> auto value_hash(T const &x) -> size_t {
282 if constexpr (requires { x.hash(); }) {
283 return x.hash();
284 } else if constexpr (std::is_arithmetic_v<T> || std::is_enum_v<T>) {
285 return hash_mix(std::hash<T>{}(x));
286 } else {
287 return std::hash<T>{}(x);
288 }
289}
290
291template <class T> auto value_hash(T *x) -> size_t {
292 if (x != nullptr) {
293 return value_hash(*x);
294 }
295 return 0;
296}
297
298template <class T> auto value_hash(std::optional<T> const &x) -> size_t {
299 if (x) {
300 return value_hash(*x);
301 }
302 return 0;
303}
304
305template <class T> auto value_hash(std::reference_wrapper<T> const &x) -> size_t {
306 return value_hash(x.get());
307}
308
309template <class T, class D> auto value_hash(std::unique_ptr<T, D> const &x) -> size_t {
310 if (x) {
311 return value_hash(*x);
312 }
313 return 0;
314}
315
316template <class T> auto value_hash(immutable_value<T> const &x) -> size_t {
317 if (x) {
318 return value_hash(*x);
319 }
320 return 0;
321}
322
323template <class T, class U> auto value_hash(std::pair<T, U> const &x) -> size_t {
324 return hash_combine(value_hash(x.first), value_hash(x.second));
325}
326
327template <class... T> auto value_hash(std::tuple<T...> const &x) -> size_t {
328 return [&x]<size_t... Indices>([[maybe_unused]] std::index_sequence<Indices...> indices) -> size_t {
329 return hash_combine(value_hash(std::get<Indices>(x))...);
330 }(std::index_sequence_for<T...>{});
331}
332
333template <class... T> auto value_hash(std::variant<T...> const &x) -> size_t {
334 return std::visit([](auto &&arg) { return hash_combine(value_hash(typeid(arg)), value_hash(arg)); }, x);
335}
336
337template <class T, size_t E> auto value_hash(std::span<T, E> const &x) -> size_t {
338 return value_hash_range(x);
339}
340
341template <class T, class A> auto value_hash(std::vector<T, A> const &x) -> size_t {
342 return value_hash_range(x);
343}
344
345template <class T> auto value_hash(Util::immutable_array<T> const &x) -> size_t {
346 return value_hash_range(x);
347}
348
349template <class T, size_t N> auto value_hash(Util::small_vector<T, N> const &x) -> size_t {
350 return value_hash_range(x);
351}
352
353inline auto value_hash(char const *x) -> size_t {
354 return std::hash<std::string_view>{}(x);
355}
356
357inline auto value_hash(std::string_view const &x) -> size_t {
358 return std::hash<std::string_view>{}(x);
359}
360
361inline auto value_hash(std::string const &x) -> size_t {
362 return std::hash<std::string_view>{}(x);
363}
364
365template <class T, class... Args> auto value_hash_record(Args const &...x) -> size_t {
366 return hash_combine(value_hash(x)...);
367}
368
369static constexpr unsigned int default_neighborhood_size = 62;
370
372
373} // namespace CppClingo::Util
Hasher for arrays of dynamic but fixed size.
Definition hash.hh:154
auto operator()(T const *sym) const -> size_t
Get the hash of the symbol array.
Definition hash.hh:159
array_hash(size_t size)
Initialize with the given size.
Definition hash.hh:157
An immutable array with efficient copying.
Definition immutable_array.hh:18
An immutable value imlementation.
Definition immutable_value.hh:19
A vector that misuses the begin, end and capacity pointers to store elements.
Definition small_vector.hh:26
auto value_hash_range(T const &x) -> size_t
Compute the hash for a given range of elements.
Definition hash.hh:243
auto value_hash(std::type_info const &x) -> size_t
Compute a hash for type_info.
Definition hash.hh:277
auto value_hash_record(Args const &...x) -> size_t
Compute and combine the hashes of the given arguments.
Definition hash.hh:365
auto hash_mix(size_t h) -> size_t
Perturb the given seed.
Definition hash.hh:220
auto hash_combine(T... a) -> size_t
Combine the given hashes.
Definition hash.hh:239
Comparison operator for arrays of dynamic but fixed size.
Definition hash.hh:168
array_equal_to(size_t size)
Initialize with the given size.
Definition hash.hh:171
auto operator()(T const *a, T const *b) const -> bool
Compare two symbols arrays.
Definition hash.hh:173
Helper class to compare pointers and some STL containers holding pointers by value.
Definition hash.hh:91
auto operator()(std::optional< T > const &a, std::optional< U > const &b) const -> bool
Compare optionals by value.
Definition hash.hh:105
auto operator()(std::unique_ptr< T, D > const &a, std::unique_ptr< U, E > const &b) const -> bool
Compare unique pointers by value.
Definition hash.hh:114
void is_transparent
Mark the comparison operator as transparent.
Definition hash.hh:93
auto operator()(T const &a, U const &b) const -> bool
Basic comparison.
Definition hash.hh:96
auto operator()(std::reference_wrapper< T > const &a, std::reference_wrapper< U > const &b) const -> bool
Compare reference wrappers by value.
Definition hash.hh:101
auto operator()(std::span< T, E > const &a, std::span< U, F > const &b) const -> bool
Compare spans by value.
Definition hash.hh:141
auto operator()(std::vector< T, A > const &a, std::vector< U, B > const &b) const -> bool
Compare vectors by value.
Definition hash.hh:146
auto operator()(char const *a, char const *b) const -> bool
Compare c-strings by value.
Definition hash.hh:98
auto operator()(std::tuple< T... > const &a, std::tuple< U... > const &b) const -> bool
Compare tuples by value.
Definition hash.hh:124
auto operator()(std::pair< T, U > const &a, std::pair< V, W > const &b) const -> bool
Compare pairs by value.
Definition hash.hh:119
auto operator()(std::variant< T... > const &a, std::variant< U... > const &b) const -> bool
Compare variants by value.
Definition hash.hh:132
auto operator()(T *a, U *b) const -> bool
Compare pointers by value.
Definition hash.hh:109
Compute a hash using std::hash.
Definition hash.hh:85
auto operator()(T const &x) const -> size_t
Compute the hash of the given value.
Definition hash.hh:87