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