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