13#if !defined(ULTRA_HASH_T_H)
16#include "utility/assert.h"
35 explicit hash_t(std::uint64_t a = 0, std::uint64_t b = 0) : data{a, b} {}
38 void clear() noexcept { data[0] = data[1] = 0; }
41 [[nodiscard]]
bool operator==(
const hash_t &)
const noexcept =
default;
43 [[nodiscard]]
bool operator<(
const hash_t &rhs)
const noexcept
45 return data[0] < rhs.data[0]
46 || (data[0] == rhs.data[0] && data[1] < rhs.data[1]);
70 data[0] = data[0] * 37 + h.data[0];
71 data[1] = data[1] * 37 + h.data[1];
85 [[nodiscard]]
bool empty() const noexcept {
return !data[0] && !data[1]; }
88 bool load(std::istream &);
89 bool save(std::ostream &)
const;
92 std::uint64_t data[2];
95std::ostream &operator<<(std::ostream &, hash_t);
107inline constexpr std::size_t murmurhash3_block_size {16};
109static constexpr std::uint64_t c1 {0x87c37b91114253d5};
110static constexpr std::uint64_t c2 {0x4cf5ad432745937f};
112inline std::uint64_t fmix(std::uint64_t k)
noexcept
116 k *= 0xff51afd7ed558ccd;
118 k *= 0xc4ceb9fe1a85ec53;
124inline std::uint32_t fmix(std::uint32_t k)
noexcept
147 h.data[0] ^= total_len;
148 h.data[1] ^= total_len;
150 h.data[0] += h.data[1];
151 h.data[1] += h.data[0];
153 h.data[0] = fmix(h.data[0]);
154 h.data[1] = fmix(h.data[1]);
156 h.data[0] += h.data[1];
157 h.data[1] += h.data[0];
171 std::uint64_t k2)
noexcept
174 k1 = std::rotl(k1, 31);
178 h.data[0] = std::rotl(h.data[0], 27);
179 h.data[0] += h.data[1];
180 h.data[0] = h.data[0] * 5 + 0x52dce729;
183 k2 = std::rotl(k2, 33);
187 h.data[1] = std::rotl(h.data[1], 31);
188 h.data[1] += h.data[0];
189 h.data[1] = h.data[1] * 5 + 0x38495ab5;
203 const std::byte *tail, std::size_t len)
noexcept
205 std::uint64_t k1(0), k2(0);
209 case 15: k2 ^= std::uint64_t(tail[14]) << 48; [[fallthrough]];
210 case 14: k2 ^= std::uint64_t(tail[13]) << 40; [[fallthrough]];
211 case 13: k2 ^= std::uint64_t(tail[12]) << 32; [[fallthrough]];
212 case 12: k2 ^= std::uint64_t(tail[11]) << 24; [[fallthrough]];
213 case 11: k2 ^= std::uint64_t(tail[10]) << 16; [[fallthrough]];
214 case 10: k2 ^= std::uint64_t(tail[ 9]) << 8; [[fallthrough]];
215 case 9: k2 ^= std::uint64_t(tail[ 8]);
216 k2 *= c2; k2 = std::rotl(k2, 33); k2 *= c1; h.data[1] ^= k2;
218 case 8: k1 ^= std::uint64_t(tail[ 7]) << 56; [[fallthrough]];
219 case 7: k1 ^= std::uint64_t(tail[ 6]) << 48; [[fallthrough]];
220 case 6: k1 ^= std::uint64_t(tail[ 5]) << 40; [[fallthrough]];
221 case 5: k1 ^= std::uint64_t(tail[ 4]) << 32; [[fallthrough]];
222 case 4: k1 ^= std::uint64_t(tail[ 3]) << 24; [[fallthrough]];
223 case 3: k1 ^= std::uint64_t(tail[ 2]) << 16; [[fallthrough]];
224 case 2: k1 ^= std::uint64_t(tail[ 1]) << 8; [[fallthrough]];
225 case 1: k1 ^= std::uint64_t(tail[ 0]);
226 k1 *= c1; k1 = std::rotl(k1, 31); k1 *= c2;
231template<std::
unsigned_
integral T>
232[[nodiscard]] T load_unaligned(
const std::byte *p)
noexcept
241 std::memcpy(&tmp, p,
sizeof(T));
261 [[nodiscard]]
static hash_t hash128(
const void *
const, std::size_t,
262 std::uint32_t = 1973)
noexcept;
274 std::uint32_t seed)
noexcept
276 Expects(data || len == 0);
278 const auto n_blocks(len / internal::murmurhash3_block_size);
281 const auto *bytes(
reinterpret_cast<const std::byte *
>(data));
285 using internal::load_unaligned;
286 for (std::size_t i(0); i < n_blocks; ++i)
288 const auto offset(i * internal::murmurhash3_block_size);
290 const auto k1(load_unaligned<std::uint64_t>(bytes + offset));
291 const auto k2(load_unaligned<std::uint64_t>(bytes + offset +
sizeof(k1)));
297 if (
const auto tail_len(len % internal::murmurhash3_block_size); tail_len)
299 const auto tail_offset(len - tail_len);
323 void write(std::span<const std::byte>)
noexcept;
324 [[nodiscard]]
hash_t final()
noexcept;
327 void process_block(
const std::byte* data)
noexcept;
331 std::array<std::byte, internal::murmurhash3_block_size> tail_ {};
332 std::size_t tail_size_ {0};
333 std::size_t total_len_ {0};
357 total_len_ += bytes.size();
362 const auto n(std::min<std::size_t>(
363 internal::murmurhash3_block_size - tail_size_,
366 std::memcpy(tail_.data() + tail_size_, bytes.data(), n);
368 bytes = bytes.subspan(n);
370 if (tail_size_ == internal::murmurhash3_block_size)
372 process_block(tail_.data());
378 while (bytes.size() >= internal::murmurhash3_block_size)
380 process_block(bytes.data());
381 bytes = bytes.subspan(internal::murmurhash3_block_size);
387 std::memcpy(tail_.data(), bytes.data(), bytes.size());
388 tail_size_ = bytes.size();
392inline void murmurhash3_sink::process_block(
const std::byte *data)
noexcept
394 const auto k1(internal::load_unaligned<std::uint64_t>(data));
395 const auto k2(internal::load_unaligned<std::uint64_t>(data +
sizeof(k1)));
425requires (std::integral<T> || std::floating_point<T>)
426[[nodiscard]] std::span<
const std::byte,
sizeof(T)>
429 return std::span<
const std::byte,
sizeof(T)>{
430 reinterpret_cast<const std::byte *
>(std::addressof(t)),
sizeof(T)};
433[[nodiscard]] std::span<const std::byte>
void write(std::span< const std::byte >) noexcept
Definition hash_t.h:355
murmurhash3_sink(std::uint32_t=1973) noexcept
Definition hash_t.h:341
hash_t final() noexcept
Definition hash_t.h:409
static hash_t hash128(const void *const, std::size_t, std::uint32_t=1973) noexcept
Definition hash_t.h:273
void process_tail(hash_t &h, const std::byte *tail, std::size_t len) noexcept
Definition hash_t.h:202
void process_block(hash_t &h, std::uint64_t k1, std::uint64_t k2) noexcept
Definition hash_t.h:170
void finalize(hash_t &h, std::size_t total_len) noexcept
Definition hash_t.h:145
std::span< const std::byte > bytes_view(const std::string &s) noexcept
Definition hash_t.cc:63
bool empty() const noexcept
We assume that a string of 128 zero bits means empty.
Definition hash_t.h:85
void clear() noexcept
Resets the content of the object.
Definition hash_t.h:38
void combine(hash_t h) noexcept
Definition hash_t.h:65
bool load(std::istream &)
Definition hash_t.cc:24
bool save(std::ostream &) const
Definition hash_t.cc:40
bool operator==(const hash_t &) const noexcept=default
Standard equality operator for hash signature.