You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fixes is_avalanching default, adds documentation for hashing
Removes is_avalanching from default implementation that uses std::hash.
That would inherit the is_avalanching when the user creates a specialization.
Adds an example test with custom hashes.
Describes hashing in the README.md in a special section, with examples
`ankerl::unordered_dense::hash` is a fast and high quality hash, based on [wyhash](https://github.com/wangyi-fudan/wyhash). The `ankerl::unordered_dense` map/set differentiates between hashes of high quality (good [avalanching effect](https://en.wikipedia.org/wiki/Avalanche_effect)) and bad quality. Hashes with good quality contain a special marker:
91
+
92
+
```cpp
93
+
using is_avalanching = void;
94
+
```
95
+
96
+
This is the cases for the specializations `bool`, `char`, `signed char`, `unsigned char`, `char8_t`, `char16_t`, `char32_t`, `wchar_t`, `short`, `unsigned short`, `int`, `unsigned int`, `long`, `long long`, `unsigned long`, `unsigned long long`, `T*`, `std::unique_ptr<T>`, `std::shared_ptr<T>`, `enum`, `std::basic_string<C>`, and `std::basic_string_view<C>`.
97
+
98
+
Hashes that do not contain such a marker are assumed to be of bad quality and receive an additional mixing step inside the map/set implementation.
99
+
100
+
#### A Simple Hash
101
+
102
+
Consider a simple custom key type:
103
+
104
+
```cpp
105
+
structid {
106
+
uint64_t value{};
107
+
108
+
auto operator==(id const& other) const -> bool {
109
+
return value == other.value;
110
+
}
111
+
};
112
+
```
113
+
114
+
The simplest implementation of a hash is this:
115
+
116
+
```cpp
117
+
structcustom_hash_simple {
118
+
auto operator()(id const& x) const noexcept -> uint64_t {
119
+
return x.value;
120
+
}
121
+
};
122
+
```
123
+
This can be used e.g. with
124
+
125
+
```cpp
126
+
auto ids = ankerl::unordered_dense::set<id, custom_hash_simple>();
127
+
```
128
+
129
+
Since `custom_hash_simple` doesn't have a `using is_avalanching = void;` marker it is considered to be of bad quality and additional mixing of `x.value` is automatically provided inside the set.
130
+
131
+
#### A High Quality Hash
132
+
133
+
Back to the `id` example, we can easily implement a higher quality hash:
134
+
135
+
```cpp
136
+
structcustom_hash_avalanching {
137
+
using is_avalanching = void;
138
+
139
+
auto operator()(id const& x) const noexcept -> uint64_t {
We know `wyhash::hash` is of high quality, so we can add `using is_avalanching = void;` which makes the map/set directly use the returned value.
146
+
147
+
148
+
#### Specialize `ankerl::unordered_dense::hash`
149
+
150
+
Instead of creating a new class you can also specialize `ankerl::unordered_dense::hash`:
151
+
152
+
```cpp
153
+
template <>
154
+
structankerl::unordered_dense::hash<id> {
155
+
using is_avalanching = void;
156
+
157
+
[[nodiscard]] auto operator()(id const& x) const noexcept -> uint64_t {
158
+
return detail::wyhash::hash(x.value);
159
+
}
160
+
};
161
+
```
162
+
163
+
#### Automatic Fallback to `std::hash`
164
+
165
+
When an implementation for `std::hash` of a custom type is available, this is automatically used and assumed to be of bad quality (thus `std::hash` is used, but an additional mixing step is performed).
166
+
167
+
168
+
#### Hash the Whole Memory
169
+
170
+
When the type [has a unique object representation](https://en.cppreference.com/w/cpp/types/has_unique_object_representations) (no padding, trivially copyable), one can just hash the object's memory. Consider a simple class
171
+
172
+
```cpp
173
+
structpoint {
174
+
int x{};
175
+
int y{};
176
+
177
+
auto operator==(point const& other) const -> bool {
178
+
return x == other.x && y == other.y;
179
+
}
180
+
};
181
+
```
182
+
183
+
A fast and high quality hash can be easily provided like so:
184
+
185
+
```cpp
186
+
structcustom_hash_unique_object_representation {
187
+
using is_avalanching = void;
188
+
189
+
[[nodiscard]] auto operator()(point const& f) const noexcept -> uint64_t {
In addition to the standard `std::unordered_map` API (see https://en.cppreference.com/w/cpp/container/unordered_map) we have additional API leveraging the fact that we're using a random access container internally:
Discards the internally held container and replaces it with the one passed. Non-unique elements are
97
211
removed, and the container will be partly reordered when non-unique elements are found.
98
212
99
-
### 3.2. Custom Container Types
213
+
### 3.3. Custom Container Types
100
214
101
215
`unordered_dense` accepts a custom allocator, but you can also specify a custom container for that template argument. That way it is possible to replace the internally used `std::vector` with e.g. `std::deque` or any other container like `boost::interprocess::vector`. This supports fancy pointers (e.g. [offset_ptr](https://www.boost.org/doc/libs/1_80_0/doc/html/interprocess/offset_ptr.html)), so the container can be used with e.g. shared memory provided by `boost::interprocess`.
102
216
103
-
### 3.3. Custom Bucket Tyeps
217
+
### 3.4. Custom Bucket Tyeps
104
218
105
219
The map/set supports two different bucket types. The default should be good for pretty much everyone.
0 commit comments