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