FE 0.15.0
A C++23 toolkit for writing compiler/interpreter frontends.
Loading...
Searching...
No Matches
hash.h
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <cstddef>
5#include <cstdint>
6
7#include <algorithm>
8#include <array>
9#include <bit>
10#include <string_view>
11
12namespace fe {
13
14static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "unsupported sizeof(size_t)");
15
16/// @name Bit Mixers
17/// These finalizers scramble a single word.
18/// They are bijective, i.e., they don't introduce any collisions on their own - they merely spread the input bits.
19///@{
20
21/// [MurmurHash3](https://en.wikipedia.org/wiki/MurmurHash)'s 32-bit finalizer `fmix32`.
22constexpr uint32_t murmur3(uint32_t h) noexcept {
23 h ^= h >> 16;
24 h *= UINT32_C(0x85ebca6b);
25 h ^= h >> 13;
26 h *= UINT32_C(0xc2b2ae35);
27 h ^= h >> 16;
28 return h;
29}
30
31/// [SplitMix64](https://prng.di.unimi.it/splitmix64.c)'s 64-bit finalizer.
32constexpr uint64_t splitmix64(uint64_t h) noexcept {
33 h ^= h >> 30;
34 h *= UINT64_C(0xbf58476d1ce4e5b9);
35 h ^= h >> 27;
36 h *= UINT64_C(0x94d049bb133111eb);
37 h ^= h >> 31;
38 return h;
39}
40
41/// Mixes @p h with murmur3 or splitmix64 - whichever matches `sizeof(size_t)`.
42constexpr size_t hash(size_t h) noexcept {
43 if constexpr (sizeof(size_t) == 4)
44 return size_t(murmur3(uint32_t(h)));
45 else
46 return size_t(splitmix64(uint64_t(h)));
47}
48///@}
49
50/// @name FNV-1 Hash
51/// See [Wikipedia](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1_hash).
52/// Use hash_begin to seed a hash chain and hash_combine to fold in one value after another:
53/// ```
54/// auto h = fe::hash_begin(x);
55/// for (auto elem : elems) h = fe::hash_combine(h, elem);
56/// ```
57/// @note These hashes are *not* stable:
58/// they differ between 32- and 64-bit builds and may change between fe releases.
59/// Never serialize them and never rely on the iteration order they induce.
60///@{
61
62// clang-format off
63/// FNV-1 [magic numbers](http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-var) for `sizeof(size_t)`.
64inline constexpr size_t fnv1_offset = sizeof(size_t) == 4 ? size_t(UINT32_C(2166136261)) : size_t(UINT64_C(14695981039346656037));
65inline constexpr size_t fnv1_prime = sizeof(size_t) == 4 ? size_t(UINT32_C( 16777619)) : size_t(UINT64_C( 1099511628211));
66// clang-format on
67
68/// Seeds a hash chain with the FNV-1 offset basis.
69constexpr size_t hash_begin() noexcept { return fnv1_offset; }
70
71/// Mixes @p v into @p seed word-wise, reusing the FNV-1 prime as multiplier.
72template<std::integral T>
73constexpr size_t hash_combine(size_t seed, T v) noexcept {
74 return hash(seed ^ (size_t(v) * fnv1_prime));
75}
76
77/// Shorthand for `hash_combine(hash_begin(), v)`.
78template<std::integral T>
79constexpr size_t hash_begin(T v) noexcept {
80 return hash_combine(hash_begin(), v);
81}
82
83/// Mixes the bytes of @p sv into @p seed a machine word at a time.
84constexpr size_t hash_combine(size_t seed, std::string_view sv) noexcept {
85 auto h = seed ^ (sv.size() * fnv1_prime); // the size too, or "a" and "a\0" would agree
86
87 for (; sv.size() >= sizeof(size_t); sv.remove_prefix(sizeof(size_t))) {
88 std::array<char, sizeof(size_t)> bytes;
89 std::copy_n(sv.begin(), bytes.size(), bytes.begin());
90 h = (h ^ std::bit_cast<size_t>(bytes)) * fnv1_prime;
91 }
92
93 if (!sv.empty()) {
94 std::array<char, sizeof(size_t)> bytes{}; // the last word is a partial one, so zero-pad it
95 std::copy(sv.begin(), sv.end(), bytes.begin());
96 h = (h ^ std::bit_cast<size_t>(bytes)) * fnv1_prime;
97 }
98
99 return hash(h); // one finalizer for the whole range instead of one per word
100}
101
102/// Shorthand for `hash_combine(hash_begin(), sv)`.
103constexpr size_t hash_begin(std::string_view sv) noexcept { return hash_combine(hash_begin(), sv); }
104///@}
105
106} // namespace fe
Definition algo.h:17
constexpr size_t hash(size_t h) noexcept
Mixes h with murmur3 or splitmix64 - whichever matches sizeof(size_t).
Definition hash.h:42
constexpr size_t fnv1_offset
FNV-1 magic numbers for sizeof(size_t).
Definition hash.h:64
constexpr size_t hash_begin() noexcept
Seeds a hash chain with the FNV-1 offset basis.
Definition hash.h:69
constexpr size_t fnv1_prime
Definition hash.h:65
constexpr uint32_t murmur3(uint32_t h) noexcept
MurmurHash3's 32-bit finalizer fmix32.
Definition hash.h:22
constexpr size_t hash_combine(size_t seed, T v) noexcept
Mixes v into seed word-wise, reusing the FNV-1 prime as multiplier.
Definition hash.h:73
constexpr uint64_t splitmix64(uint64_t h) noexcept
SplitMix64's 64-bit finalizer.
Definition hash.h:32