/src/glaze/include/glaze/api/hash.hpp
Line | Count | Source |
1 | | // Glaze Library |
2 | | // For the license information refer to glaze.hpp |
3 | | |
4 | | #pragma once |
5 | | |
6 | | #include <climits> |
7 | | |
8 | | #include "glaze/api/xxh64.hpp" |
9 | | #include "glaze/core/meta.hpp" |
10 | | |
11 | | // Collision calculations done with the formula: e^((-k * (k - 1)/(2 * N))) |
12 | | // The approximation error tends to zero as N increases, and we are dealing with a large N |
13 | | // https://preshing.com/20110504/hash-collision-probabilities/ |
14 | | // With 10,000 registered types the probabilities of a collision are: |
15 | | // 64 bit hash: 2.71027645e-12 === 1 - 1/e^(6249375/2305843009213693952) |
16 | | // 128 bit hash: 1.469221e-31 === 1 - 1/e^(6249375/42535295865117307932921825928971026432) |
17 | | // 256 bit hash: 4.317652e-70 === 1 - |
18 | | // 1/e^(6249375/14474011154664524427946373126085988481658748083205070504932198000989141204992) Probability of winning |
19 | | // the Mega Millions lottery: 1 / 302,575,350 === 3.30496189e-9 Number of times winning to equal a collision chance: 64 |
20 | | // bit: 1220 times 128 bit: 2.2494655e22 times (or 22 sextillion times) 256 bit: 7.6545351e60 times From these |
21 | | // calculations it is apparent that a 128 bit hash is more than sufficient |
22 | | |
23 | | namespace glz |
24 | | { |
25 | | template <class T, T Value> |
26 | | consteval auto make_array() |
27 | | { |
28 | | return []<size_t... I>(std::index_sequence<I...>) { |
29 | | return std::array<char, sizeof(T)>{static_cast<char>(((Value >> (CHAR_BIT * I)) & 0xff))...}; |
30 | | }(std::make_index_sequence<sizeof(T)>{}); |
31 | | } |
32 | | |
33 | | namespace detail |
34 | | { |
35 | | // convert an integer to a string_view at compile time |
36 | | |
37 | | constexpr uint64_t num_digits(auto x) noexcept // number of digits needed, including minus sign |
38 | 0 | { |
39 | 0 | return x < 10 ? 1 : 1 + num_digits(x / 10); |
40 | 0 | } |
41 | | |
42 | | template <char... args> |
43 | | struct metastring |
44 | | { |
45 | | const char data[sizeof...(args)] = {args...}; |
46 | | }; |
47 | | |
48 | | // recursive number-printing template, general case (for three or more digits) |
49 | | template <uint64_t size, uint64_t x, char... args> |
50 | | struct numeric_builder |
51 | | { |
52 | | using type = typename numeric_builder<size - 1, x / 10, '0' + x % 10, args...>::type; |
53 | | }; |
54 | | |
55 | | // special case for two digits; minus sign is handled here |
56 | | template <uint64_t x, char... args> |
57 | | struct numeric_builder<2, x, args...> |
58 | | { |
59 | | using type = metastring<'0' + x / 10, '0' + x % 10, args...>; |
60 | | }; |
61 | | |
62 | | // special case for one digit (positive numbers only) |
63 | | template <uint64_t x, char... args> |
64 | | struct numeric_builder<1, x, args...> |
65 | | { |
66 | | using type = metastring<'0' + x, args...>; |
67 | | }; |
68 | | |
69 | | // convenience wrapper for numeric_builder |
70 | | template <uint64_t x> |
71 | | struct numeric_string |
72 | | { |
73 | | // generate a unique string type representing this number |
74 | | using type = typename numeric_builder<num_digits(x), x, '\0'>::type; |
75 | | |
76 | | // declare a static string of that type (instantiated later at file scope) |
77 | | static constexpr type value{}; |
78 | | static constexpr const std::string_view get() { return {value.data, num_digits(x)}; } |
79 | | }; |
80 | | } |
81 | | |
82 | | template <class T, T Value> |
83 | | struct int_to_sv |
84 | | { |
85 | | static constexpr auto arr = make_array<T, Value>(); |
86 | | static consteval std::string_view get() { return {arr.data(), arr.size()}; } |
87 | | }; |
88 | | |
89 | | template <class T, T Value> |
90 | | inline constexpr std::string_view int_to_sv_v = int_to_sv<T, Value>{}.get(); |
91 | | |
92 | | template <uint64_t I> |
93 | | inline consteval auto to_sv() |
94 | | { |
95 | | return detail::numeric_string<I>::get(); |
96 | | } |
97 | | |
98 | | template <size_t I> |
99 | | struct hash128_i |
100 | | { |
101 | | static constexpr sv str = to_sv<I>(); |
102 | | static constexpr sv h0 = int_to_sv_v<uint64_t, xxh64::hash(str.data(), str.size(), 0)>; |
103 | | static constexpr sv h1 = int_to_sv_v<uint64_t, xxh64::hash(str.data(), str.size(), 1)>; |
104 | | static constexpr sv value = join_v<h0, h1>; |
105 | | }; |
106 | | |
107 | | template <size_t I> |
108 | | inline constexpr std::string_view hash128_i_v = hash128_i<I>::value; |
109 | | |
110 | | template <const std::string_view& Str> |
111 | | struct hash128 |
112 | | { |
113 | | static constexpr sv h0 = int_to_sv_v<uint64_t, xxh64::hash(Str.data(), Str.size(), 0)>; |
114 | | static constexpr sv h1 = int_to_sv_v<uint64_t, xxh64::hash(Str.data(), Str.size(), 1)>; |
115 | | static constexpr sv value = join_v<h0, h1>; |
116 | | }; |
117 | | |
118 | | template <const std::string_view& Str> |
119 | | constexpr std::string_view hash128_v = hash128<Str>::value; |
120 | | } |