OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
hash.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
4
5#include <rapidhash.h>
6
7#include <cinttypes>
8#include <cstdint>
9#include <memory>
10#include <string>
11#include <string_view>
12#include <type_traits>
13
14namespace ossia
15{
16
17OSSIA_INLINE inline uint64_t hash_bytes(const void* data, std::size_t size) noexcept;
18OSSIA_INLINE inline uint64_t hash_string(std::string_view sv) noexcept;
19template <typename T>
20OSSIA_INLINE inline uint64_t hash_trivial(const T& v) noexcept;
21
22struct string_hash
23{
24 using is_transparent = std::true_type;
25 using is_avalanching = std::true_type;
26 std::size_t operator()(const std::string& s) const noexcept
27 {
28 return hash_string(std::string_view{s});
29 }
30
31 std::size_t operator()(std::string_view s) const noexcept
32 {
33 return hash_string(s);
34 }
35
36 template <std::size_t N>
37 std::size_t operator()(const char (&s)[N]) const noexcept
38 {
39 return hash_string(std::string_view{s, N - 1});
40 }
41};
42
43// https://stackoverflow.com/q/20953390/1495627
44struct egur_hash
45{
46 using is_transparent = std::true_type;
47
48 template <typename T>
49 OSSIA_INLINE OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK std::size_t
50 operator()(const T* val) const noexcept
51 {
52 static const constexpr std::size_t shift = constexpr_log2(1 + sizeof(T));
53 return (size_t)(val) >> shift;
54 }
55
56 template <typename T>
57 OSSIA_INLINE OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK std::size_t
58 operator()(const std::shared_ptr<T>& val) const noexcept
59 {
60 static const constexpr std::size_t shift = constexpr_log2(1 + sizeof(T));
61 return (size_t)(val.get()) >> shift;
62 }
63};
64
65template <std::size_t ApproximateObjectSizeof>
66struct unknown_pointer_hash
67{
68 using is_transparent = std::true_type;
69
70 template <typename T>
71 OSSIA_INLINE OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK std::size_t
72 operator()(const T* val) const noexcept
73 {
74 static const constexpr std::size_t shift
75 = constexpr_log2(1 + ApproximateObjectSizeof);
76 return (size_t)(val) >> shift;
77 }
78
79 template <typename T>
80 OSSIA_INLINE OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK std::size_t
81 operator()(const std::shared_ptr<T>& val) const noexcept
82 {
83 static const constexpr std::size_t shift
84 = constexpr_log2(1 + ApproximateObjectSizeof);
85 return (size_t)(val.get()) >> shift;
86 }
87};
88
89template<typename T> struct is_shared_ptr : std::false_type {};
90template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
91
92struct pointer_hash_rapid
93{
94 using is_transparent = std::true_type;
95 using is_avalanching = std::true_type;
96 template <typename T>
97 OSSIA_INLINE uint64_t operator()(const T* val) const noexcept
98 {
99 const uintptr_t p = reinterpret_cast<uintptr_t>(val);
100 return hash_trivial(p);
101 }
102
103 template <typename T>
104 OSSIA_INLINE uint64_t operator()(const std::shared_ptr<T>& val) const noexcept
105 {
106 const uintptr_t p = reinterpret_cast<uintptr_t>(val.get());
107 return hash_trivial(p);
108 }
109};
110
111namespace detail_hash
112{
113template <typename T>
114concept has_std_hash = requires(const T& v) {
115 { std::hash<T>{}(v) } -> std::convertible_to<std::size_t>;
116};
117template <typename T>
118concept has_std_hash_avalanching
119 = has_std_hash<T> && requires { typename std::hash<T>::is_avalanching; };
120}
121template <typename T>
122struct hash
123{
124 OSSIA_INLINE std::size_t operator()(const T& v) const
125 noexcept(noexcept(std::declval<std::hash<T>>()(std::declval<const T&>())))
126 {
127 static_assert(
128 detail_hash::has_std_hash<T>,
129 "ossia::hash<T>: T is not trivially copyable and has no "
130 "std::hash<T> specialization. Specialize ossia::hash<T> "
131 "(see string_hash / pointer_hash_rapid for reference) or "
132 "provide std::hash<T>.");
133 return std::hash<T>{}(v);
134 }
135};
136template <typename T>
137requires std::is_trivially_copyable_v<T>
138 && (!std::is_pointer_v<T>)
139 && (!is_shared_ptr<T>::value)
140struct hash<T>
141{
142 using is_transparent = std::true_type;
143 using is_avalanching = std::true_type;
144 OSSIA_INLINE uint64_t operator()(const T& v) const noexcept
145 {
146 return hash_trivial(v);
147 }
148};
149
150template <typename T>
151requires (!std::is_trivially_copyable_v<T>)
152 && (!std::is_pointer_v<T>)
153 && (!is_shared_ptr<T>::value)
154 && (!std::is_same_v<T, std::string>)
155 && (!std::is_same_v<T, std::string_view>)
156 && detail_hash::has_std_hash_avalanching<T>
157struct hash<T>
158{
159 using is_avalanching = std::true_type;
160
161 OSSIA_INLINE std::size_t operator()(const T& v) const
162 noexcept(noexcept(std::declval<std::hash<T>>()(std::declval<const T&>())))
163 {
164 return std::hash<T>{}(v);
165 }
166};
167
168template <typename T>
169requires std::is_pointer_v<T>
170struct hash<T> : pointer_hash_rapid { };
171
172template <typename T>
173requires is_shared_ptr<T>::value
174struct hash<T> : pointer_hash_rapid { };
175
176template <>
177struct hash<std::string> : string_hash { };
178template <>
179struct hash<std::string_view> : string_hash { };
180
181// Invalid overloads
182template <>
183struct hash<const char*>;
184template <>
185struct hash<char*>;
186
187
188
189struct string_equal
190{
191 using is_transparent = std::true_type;
192 bool operator()(const std::string& s, const std::string& s2) const noexcept
193 {
194 return s == s2;
195 }
196 bool operator()(std::string_view s, const std::string& s2) const noexcept
197 {
198 return s == s2;
199 }
200 bool operator()(const std::string& s, std::string_view s2) const noexcept
201 {
202 return s == s2;
203 }
204 bool operator()(std::string_view s, std::string_view s2) const noexcept
205 {
206 return s == s2;
207 }
208
209 template <std::size_t N>
210 bool operator()(const std::string& s, const char (&s2)[N]) const noexcept
211 {
212 return operator()(s, std::string_view{s2, N-1});
213 }
214
215 template <std::size_t N>
216 bool operator()(std::string_view s, const char (&s2)[N]) const noexcept
217 {
218 return operator()(s, std::string_view{s2, N-1});
219 }
220
221 template <std::size_t N>
222 bool operator()(const char (&s)[N], const std::string& s2) const noexcept
223 {
224 return operator()(std::string_view{s, N-1}, s2);
225 }
226
227 template <std::size_t N>
228 bool operator()(const char (&s)[N], std::string_view s2) const noexcept
229 {
230 return operator()(std::string_view{s, N-1}, s2);
231 }
232};
233
234
235struct pointer_equal
236{
237 using is_transparent = std::true_type;
238 template<typename U, typename V>
239 OSSIA_INLINE
240 bool operator()(const U* lhs, const V* rhs) const noexcept { return lhs == rhs; }
241
242 template<typename U, typename V>
243 OSSIA_INLINE
244 bool operator()(const std::shared_ptr<U>& lhs, const V* rhs) const noexcept
245 {
246 return lhs.get() == rhs;
247 }
248
249 template<typename U, typename V>
250 OSSIA_INLINE
251 bool operator()(const U* lhs, const std::shared_ptr<V>& rhs) const noexcept
252 {
253 return lhs == rhs.get();
254 }
255
256 template<typename U, typename V>
257 OSSIA_INLINE
258 bool
259 operator()(const std::shared_ptr<U>& lhs, const std::shared_ptr<V>& rhs) const noexcept
260 {
261 return lhs == rhs;
262 }
263};
264
265template <typename T>
266struct equal_to : std::equal_to<T> { };
267
268template <>
269struct equal_to<std::string> : string_equal { };
270template <>
271struct equal_to<std::string_view> : string_equal { };
272
273template <typename T>
274requires std::is_pointer_v<T>
275struct equal_to<T> : pointer_equal { };
276
277template <typename T>
278requires is_shared_ptr<T>::value
279struct equal_to<T> : pointer_equal { };
280
281OSSIA_INLINE inline uint64_t hash_bytes(const void* data, std::size_t size) noexcept
282{
283 return rapidhashMicro(data, size);
284}
285template <typename T>
286OSSIA_INLINE inline uint64_t hash_trivial(const T& v) noexcept
287{
288 static_assert(std::is_trivially_copyable_v<T>,
289 "hash_trivial requires trivially copyable T");
290 if constexpr(sizeof(T) <= 48)
291 return rapidhashNano(&v, sizeof(T));
292 else if constexpr(sizeof(T) <= 80)
293 return rapidhashMicro(&v, sizeof(T));
294 else
295 return rapidhash(&v, sizeof(T));
296}
297OSSIA_INLINE inline uint64_t hash_string(std::string_view sv) noexcept
298{
299 return rapidhashMicro(sv.data(), sv.size());
300}
301// hash_combine_impl taken from boost
302template <typename T>
303OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK constexpr inline void
304hash_combine(std::size_t& seed, const T& k) noexcept
305{
306 using namespace std;
307 seed ^= hash<T>{}(k) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
308}
309
310template <typename T>
311OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK constexpr inline void
312hash_combine(std::size_t& seed, const T* k) noexcept
313{
314 using namespace std;
315 seed ^= egur_hash{}(k) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
316}
317
318OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
319constexpr inline void hash_combine(uint64_t& seed, uint8_t k) noexcept
320{
321 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
322}
323
324OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
325constexpr inline void hash_combine(uint64_t& seed, int8_t k) noexcept
326{
327 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
328}
329
330OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
331constexpr inline void hash_combine(uint64_t& seed, uint16_t k) noexcept
332{
333 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
334}
335
336OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
337constexpr inline void hash_combine(uint64_t& seed, int16_t k) noexcept
338{
339 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
340}
341
342OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
343constexpr inline void hash_combine(uint64_t& seed, uint32_t k) noexcept
344{
345 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
346}
347
348OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
349constexpr inline void hash_combine(uint64_t& seed, int32_t k) noexcept
350{
351 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
352}
353
354OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
355constexpr inline void hash_combine(uint64_t& seed, int64_t k) noexcept
356{
357 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
358}
359
360OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
361constexpr inline void hash_combine(uint32_t& seed, uint8_t k) noexcept
362{
363 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
364}
365
366OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
367constexpr inline void hash_combine(uint32_t& seed, int8_t k) noexcept
368{
369 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
370}
371
372OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
373constexpr inline void hash_combine(uint32_t& seed, uint16_t k) noexcept
374{
375 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
376}
377
378OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
379constexpr inline void hash_combine(uint32_t& seed, int16_t k) noexcept
380{
381 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
382}
383
384OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
385constexpr inline void hash_combine(uint32_t& seed, int32_t k) noexcept
386{
387 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
388}
389
390OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
391constexpr inline void hash_combine(uint32_t& seed, uint64_t k) noexcept
392{
393 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
394}
395
396OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
397constexpr inline void hash_combine(uint32_t& seed, int64_t k) noexcept
398{
399 seed ^= k + 0x9e3779b9 + (seed << 6) + (seed >> 2);
400}
401
402OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
403constexpr inline void hash_combine(uint32_t& h1, uint32_t k1) noexcept
404{
405 constexpr auto rotl32
406 = [](uint32_t x, int8_t r) noexcept { return (x << r) | (x >> (32 - r)); };
407
408 constexpr uint32_t c1 = 0xcc9e2d51;
409 constexpr uint32_t c2 = 0x1b873593;
410
411 k1 *= c1;
412 k1 = rotl32(k1, 15);
413 k1 *= c2;
414
415 h1 ^= k1;
416 h1 = rotl32(h1, 13);
417 h1 = h1 * 5 + 0xe6546b64;
418}
419
420OSSIA_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
421constexpr inline void hash_combine(uint64_t& h, uint64_t k) noexcept
422{
423 constexpr auto m = UINT64_C(0xc6a4a7935bd1e995);
424 constexpr int r = 47;
425
426 k *= m;
427 k ^= k >> r;
428 k *= m;
429
430 h ^= k;
431 h *= m;
432
433 // Completely arbitrary number, to prevent 0's
434 // from hashing to 0.
435 h += 0xe6546b64;
436}
437}
Definition git_info.h:7