dice-hash
Hash function for stl types and container
Loading...
Searching...
No Matches
martinus_robinhood_hash.hpp
1#ifndef HYPERTRIE_MARTINUS_ROBINHOOD_HASH_HPP
2#define HYPERTRIE_MARTINUS_ROBINHOOD_HASH_HPP
3// ______ _____ ______ _________
4// ______________ ___ /_ ___(_)_______ ___ /_ ______ ______ ______ /
5// __ ___/_ __ \__ __ \__ / __ __ \ __ __ \_ __ \_ __ \_ __ /
6// _ / / /_/ /_ /_/ /_ / _ / / / _ / / // /_/ // /_/ // /_/ /
7// /_/ \____/ /_.___/ /_/ /_/ /_/ ________/_/ /_/ \____/ \____/ \__,_/
8// _/_____/
9//
10// Fast & memory efficient hashtable based on robin hood hashing for C++11/14/17/20
11// https://github.com/martinus/robin-hood-hashing
12//
13// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
14// SPDX-License-Identifier: MIT
15// Copyright (c) 2018-2020 Martin Ankerl <http://martin.ankerl.com>
16//
17// Permission is hereby granted, free of charge, to any person obtaining a copy
18// of this software and associated documentation files (the "Software"), to deal
19// in the Software without restriction, including without limitation the rights
20// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21// copies of the Software, and to permit persons to whom the Software is
22// furnished to do so, subject to the following conditions:
23//
24// The above copyright notice and this permission notice shall be included in all
25// copies or substantial portions of the Software.
26//
27// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33// SOFTWARE.
34
35#include <cstddef>
36#include <cstdint>
37#include <cstring>
38#include <initializer_list>
39
40
41namespace dice::hash::martinus {
42
43 inline constexpr std::size_t m = 0xc6a4a7935bd1e995UL;
44 inline constexpr std::size_t seed = 0xe17a1465UL;
45 inline constexpr unsigned int r = 47;
46
47
48 template<typename T>
49 inline T unaligned_load(void const *ptr) noexcept {
50 // using memcpy so we don't get into unaligned load problems.
51 // compiler should optimize this very well anyways.
52 T t;
53 std::memcpy(&t, ptr, sizeof(T));
54 return t;
55 }
56
57 template<typename T>
58 inline T rotr(T x, unsigned k) {
59 return (x >> k) | (x << (8U * sizeof(T) - k));
60 }
61
62 inline std::size_t hash_bytes(void const *ptr, std::size_t len) noexcept {
63
64
65 static constexpr unsigned int r = 47;
66
67 auto const *const data64 = static_cast<uint64_t const *>(ptr);
68 uint64_t h = seed ^ (len * m);
69
70 size_t const n_blocks = len / 8;
71 for (std::size_t i = 0; i < n_blocks; ++i) {
72 auto k = unaligned_load<uint64_t>(data64 + i);
73
74 k *= m;
75 k ^= k >> r;
76 k *= m;
77
78 h ^= k;
79 h *= m;
80 }
81
82 auto const *const data8 = reinterpret_cast<uint8_t const *>(data64 + n_blocks);
83 switch (len & 7U) {
84 case 7:
85 h ^= static_cast<uint64_t>(data8[6]) << 48U;
86 [[fallthrough]];
87 case 6:
88 h ^= static_cast<uint64_t>(data8[5]) << 40U;
89 [[fallthrough]];
90 case 5:
91 h ^= static_cast<uint64_t>(data8[4]) << 32U;
92 [[fallthrough]];
93 case 4:
94 h ^= static_cast<uint64_t>(data8[3]) << 24U;
95 [[fallthrough]];
96 case 3:
97 h ^= static_cast<uint64_t>(data8[2]) << 16U;
98 [[fallthrough]];
99 case 2:
100 h ^= static_cast<uint64_t>(data8[1]) << 8U;
101 [[fallthrough]];
102 case 1:
103 h ^= static_cast<uint64_t>(data8[0]);
104 h *= m;
105 [[fallthrough]];
106 default:
107 break;
108 }
109
110 h ^= h >> r;
111 h *= m;
112 h ^= h >> r;
113 return static_cast<size_t>(h);
114 }
115
116 inline std::size_t hash_combine(std::initializer_list<size_t> hashes) {
117
118
119 uint64_t h = seed ^ (hashes.size() * m);
120
121 for (auto k : hashes) {
122 k *= m;
123 k ^= k >> r;
124 k *= m;
125
126 h ^= k;
127 h *= m;
128 }
129
130 h ^= h >> r;
131 h *= m;
132 h ^= h >> r;
133 return static_cast<size_t>(h);
134 }
135
136 class HashState {
137
138 size_t h;
139
140 public:
141 explicit HashState(uint64_t size) : h(seed ^ (size * m)) {}
142
143 void add(std::size_t hash) noexcept {
144 hash *= m;
145 hash ^= hash >> r;
146 hash *= m;
147
148 h ^= hash;
149 h *= m;
150 }
151
152 [[nodiscard]] std::size_t digest() const noexcept {
153 size_t hash = h;
154 hash ^= hash >> r;
155 hash *= m;
156 hash ^= hash >> r;
157 return static_cast<size_t>(hash);
158 }
159 };
160
161 inline std::size_t hash_int(uint64_t x) noexcept {
162 // inspired by lemire's strongly universal hashing
163 // https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/
164 //
165 // Instead of shifts, we use rotations so we don't lose any bits.
166 //
167 // Added a final multiplication with a constant for more mixing. It is most important that
168 // the lower bits are well mixed.
169 auto h1 = x * 0xA24BAED4963EE407UL;
170 auto h2 = rotr(x, 32U) * 0x9FB21C651E98DF25UL;
171 auto h = rotr(h1 + h2, 32U);
172 return static_cast<size_t>(h);
173 }
174}// namespace dice::hash::martinus
175
176#endif//HYPERTRIE_MARTINUS_ROBINHOOD_HASH_HPP
Definition martinus_robinhood_hash.hpp:136