HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
sip_hash.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#include "random/seed.hpp"
6#include "utility/module.hpp"
7#include <string_view>
8#include <string>
9#include <span>
10
11namespace hi::inline v1 {
12namespace detail {
13
15 uint64_t k0;
16 uint64_t k1;
17
18 sip_hash_seed_type(uint64_t k0, uint64_t k1) noexcept : k0(k0), k1(k1) {}
19
21};
22
23inline auto sip_hash_seed = sip_hash_seed_type();
24
26
27} // namespace detail
28
29template<size_t C, size_t D>
30class sip_hash {
31public:
32 constexpr sip_hash(sip_hash const&) noexcept = default;
33 constexpr sip_hash(sip_hash&&) noexcept = default;
34 constexpr sip_hash& operator=(sip_hash const&) noexcept = default;
35 constexpr sip_hash& operator=(sip_hash&&) noexcept = default;
36
37 sip_hash(detail::sip_hash_seed_tag) noexcept : sip_hash(detail::sip_hash_seed.k0, detail::sip_hash_seed.k1) {}
38
41 sip_hash() noexcept;
42
43 constexpr sip_hash(uint64_t k0, uint64_t k1) noexcept :
44 _v0(k0 ^ 0x736f6d6570736575),
45 _v1(k1 ^ 0x646f72616e646f6d),
46 _v2(k0 ^ 0x6c7967656e657261),
47 _v3(k1 ^ 0x7465646279746573),
48 _m(0),
49 _b(0)
50 {
51#ifndef NDEBUG
52 _debug_state = debug_state_type::idle;
53#endif
54 }
55
56 [[nodiscard]] uint64_t finish() noexcept
57 {
58#ifndef NDEBUG
59 hi_assert(_debug_state < debug_state_type::finalized);
60 _debug_state = debug_state_type::finalized;
61#endif
62
63 auto v0 = _v0;
64 auto v1 = _v1;
65 auto v2 = _v2;
66 auto v3 = _v3;
67 auto m = _m;
68 auto b = _b;
69
70 // Add the length modulo 256 to the end of the last block.
71 m |= static_cast<uint64_t>(b) << 56;
72 _compress(v0, v1, v2, v3, m);
73 _finalize(v0, v1, v2, v3);
74
75 return v0 ^ v1 ^ v2 ^ v3;
76 }
77
78 void add(void const *data, size_t size) noexcept
79 {
80#ifndef NDEBUG
81 hi_assert(_debug_state <= debug_state_type::partial);
82 _debug_state = debug_state_type::partial;
83#endif
84 auto todo = size;
85 auto *src = static_cast<char const *>(data);
87
88 auto v0 = _v0;
89 auto v1 = _v1;
90 auto v2 = _v2;
91 auto v3 = _v3;
92 auto m = _m;
93
94 // If a partial 64-bit word was already submitted, complete that word.
95 if (hilet offset = _b & 7) {
96 hilet num_bytes = std::min(8_uz - offset, size);
97
98 // Accumulate remaining bytes in m.
99 for (auto i = offset; i != offset + num_bytes; ++i) {
100 m |= char_cast<uint64_t>(src[i]) << (i * CHAR_BIT);
101 }
102
103 if (offset + num_bytes == 8) {
104 _compress(v0, v1, v2, v3, std::exchange(m, 0));
105 }
106
107 todo -= num_bytes;
108 src += num_bytes;
109 }
110
111 // Now we can compress 64 bits at a time.
112 while (todo >= 8) {
113 m = load_le<uint64_t>(src);
114 src += 8;
115 todo -= 8;
116 _compress(v0, v1, v2, v3, std::exchange(m, 0));
117 }
118
119 // Add the incomplete word in the state, to be compressed later.
120 for (auto i = 0_uz; i != todo; ++i) {
121 m |= char_cast<uint64_t>(src[i]) << (i * CHAR_BIT);
122 }
123
124 _v0 = v0;
125 _v1 = v1;
126 _v2 = v2;
127 _v3 = v3;
128 _m = m;
129 _b = truncate<uint8_t>(_b + size);
130 }
131
141 [[nodiscard]] uint64_t complete_message(void const *data, size_t size) const noexcept
142 {
143 auto *src = static_cast<char const *>(data);
145
146#ifndef NDEBUG
147 hi_assert(_debug_state == debug_state_type::idle);
148#endif
149
150 auto v0 = _v0;
151 auto v1 = _v1;
152 auto v2 = _v2;
153 auto v3 = _v3;
154 uint64_t m;
155
156 for (auto block_count = size / 8; block_count > 0; --block_count, src += 8) {
157 m = load_le<uint64_t>(src);
158 _compress(v0, v1, v2, v3, m);
159 }
160
161 // The length, and 0 to 7 of the last bytes from the src.
162 m = wide_cast<uint64_t>(size & 0xff) << 56;
163
164 for (auto i = 0_uz; i != (size & 7); ++i) {
165 m |= char_cast<uint64_t>(src[i]) << (i * CHAR_BIT);
166 }
167 _compress(v0, v1, v2, v3, m);
168 _finalize(v0, v1, v2, v3);
169
170 return v0 ^ v1 ^ v2 ^ v3;
171 }
172
177 [[nodiscard]] uint64_t operator()(void const *data, size_t size) const noexcept
178 {
179 return complete_message(data, size);
180 }
181
182private:
183 uint64_t _v0;
184 uint64_t _v1;
185 uint64_t _v2;
186 uint64_t _v3;
187
188 uint64_t _m;
189 uint8_t _b;
190#ifndef NDEBUG
191 enum class debug_state_type : uint8_t { idle, full, partial, finalized };
192 debug_state_type _debug_state;
193#endif
194
195 hi_force_inline static constexpr void _round(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3) noexcept
196 {
197 v0 += v1;
198 v2 += v3;
199 v1 = std::rotl(v1, 13);
200 v3 = std::rotl(v3, 16);
201 v1 ^= v0;
202 v3 ^= v2;
203 v0 = std::rotl(v0, 32);
204
205 v0 += v3;
206 v2 += v1;
207 v1 = std::rotl(v1, 17);
208 v3 = std::rotl(v3, 21);
209 v1 ^= v2;
210 v3 ^= v0;
211 v2 = std::rotl(v2, 32);
212 }
213
214 static constexpr void _compress(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3, uint64_t m) noexcept
215 {
216 hilet m_ = m;
217
218 v3 ^= m_;
219 for (auto i = 0_uz; i != C; ++i) {
220 _round(v0, v1, v2, v3);
221 }
222 v0 ^= m_;
223 }
224
225 static constexpr void _finalize(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3) noexcept
226 {
227 v2 ^= 0xff;
228 for (auto i = 0_uz; i != D; ++i) {
229 _round(v0, v1, v2, v3);
230 }
231 }
232};
233
234namespace detail {
235template<size_t C, size_t D>
236static inline sip_hash sip_hash_prototype = sip_hash<C, D>(sip_hash_seed_tag{});
237}
238
239template<size_t C, size_t D>
240sip_hash<C, D>::sip_hash() noexcept : sip_hash(detail::sip_hash_prototype<C, D>)
241{
242}
243
245
246template<typename T>
248 [[nodiscard]] uint64_t operator()(T const& rhs) const noexcept
249 {
251 }
252
253 [[nodiscard]] uint64_t operator()(T const& rhs) const noexcept
254 requires(std::has_unique_object_representations_v<T> and not std::is_pointer_v<T>)
255 {
256 return _sip_hash24{}(&rhs, sizeof(rhs));
257 }
258};
259
260template<typename CharT, typename CharTrait>
261struct sip_hash24<std::basic_string_view<CharT, CharTrait>> {
262 [[nodiscard]] uint64_t operator()(std::basic_string_view<CharT, CharTrait> const& rhs) const noexcept
263 {
264 return _sip_hash24{}(rhs.data(), rhs.size());
265 }
266};
267
268template<typename CharT, typename CharTrait>
269struct sip_hash24<std::basic_string<CharT, CharTrait>> {
270 [[nodiscard]] uint64_t operator()(std::basic_string<CharT, CharTrait> const& rhs) const noexcept
271 {
272 return _sip_hash24{}(rhs.data(), rhs.size());
273 }
274};
275
276template<typename T>
277struct sip_hash24<std::span<T>> {
278 [[nodiscard]] uint64_t operator()(std::span<T> const& rhs) const noexcept
279 {
280 return _sip_hash24{}(rhs.data(), rhs.size());
281 }
282};
283
284template<>
285struct sip_hash24<char *> {
286 [[nodiscard]] uint64_t operator()(char const *rhs) const noexcept
287 {
288 hilet length = strlen(rhs);
289 return _sip_hash24{}(rhs, length * sizeof(char));
290 }
291};
292
293template<>
294struct sip_hash24<wchar_t *> {
295 [[nodiscard]] uint64_t operator()(wchar_t const *rhs) const noexcept
296 {
297 hilet length = wcslen(rhs);
298 return _sip_hash24{}(rhs, length * sizeof(wchar_t));
299 }
300};
301
302} // namespace hi::inline v1
Cryptographically secure entropy.
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hi_static_not_implemented(...)
This part of the code has not been implemented yet.
Definition assert.hpp:342
#define hi_axiom_not_null(expression,...)
Assert if an expression is not nullptr.
Definition assert.hpp:272
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
Randomly generate an object.
Definition seed.hpp:35
Definition sip_hash.hpp:14
Definition sip_hash.hpp:25
Definition sip_hash.hpp:30
uint64_t operator()(void const *data, size_t size) const noexcept
Hash a complete message.
Definition sip_hash.hpp:177
uint64_t complete_message(void const *data, size_t size) const noexcept
Hash a complete message.
Definition sip_hash.hpp:141
Definition sip_hash.hpp:247
T data(T... args)
T min(T... args)
T size(T... args)