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