blob: a148f2cb49d506e6535b1a84475cd3107bd2ccb1 [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2021 The Dawn & Tint Authors
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
Ryan Harrisondbc13af2022-02-21 15:19:07 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ryan Harrisondbc13af2022-02-21 15:19:07 +000027
dan sinclair22b4dd22023-07-21 00:40:07 +000028#ifndef SRC_TINT_UTILS_MATH_HASH_H_
29#define SRC_TINT_UTILS_MATH_HASH_H_
Ryan Harrisondbc13af2022-02-21 15:19:07 +000030
31#include <stdint.h>
32#include <cstdio>
33#include <functional>
Ben Claytonb703afc2023-05-05 18:55:15 +000034#include <string>
Ben Claytonb0664682022-05-19 17:59:19 +000035#include <tuple>
Ben Clayton046abc02022-04-28 13:08:22 +000036#include <utility>
Ben Clayton0c7c23b2022-08-31 23:51:48 +000037#include <variant>
Ryan Harrisondbc13af2022-02-21 15:19:07 +000038#include <vector>
39
dan sinclair22b4dd22023-07-21 00:40:07 +000040#include "src/tint/utils/math/crc32.h"
Ben Clayton3bc20e32022-07-21 19:34:05 +000041
dan sinclairbae54e72023-07-28 15:01:54 +000042namespace tint {
Ben Clayton76aec252024-02-05 20:49:36 +000043
Ryan Harrisondbc13af2022-02-21 15:19:07 +000044namespace detail {
45
Ben Clayton4dcbdda2023-09-20 14:51:34 +000046template <typename T, typename = void>
47struct HasHashCodeMember : std::false_type {};
48
49template <typename T>
50struct HasHashCodeMember<
51 T,
52 std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::HashCode)>>> : std::true_type {
53};
54
Ryan Harrisondbc13af2022-02-21 15:19:07 +000055} // namespace detail
56
Ben Clayton76aec252024-02-05 20:49:36 +000057/// The type of a hash code
58using HashCode = uint32_t;
59
Ben Claytone3f20052022-08-23 16:10:35 +000060/// Forward declarations (see below)
Ben Claytonb0664682022-05-19 17:59:19 +000061template <typename... ARGS>
Ben Clayton76aec252024-02-05 20:49:36 +000062HashCode Hash(const ARGS&... values);
Ben Claytonb0664682022-05-19 17:59:19 +000063
Ben Claytone3f20052022-08-23 16:10:35 +000064template <typename... ARGS>
Ben Clayton76aec252024-02-05 20:49:36 +000065HashCode HashCombine(HashCode hash, const ARGS&... values);
Ryan Harrisondbc13af2022-02-21 15:19:07 +000066
Ben Claytone3f20052022-08-23 16:10:35 +000067/// A STL-compatible hasher that does a more thorough job than most implementations of std::hash.
68/// Hasher has been optimized for a better quality hash at the expense of increased computation
69/// costs.
Ben Clayton4dcbdda2023-09-20 14:51:34 +000070/// Hasher is specialized for various core Tint data types. The default implementation will use a
Ben Clayton76aec252024-02-05 20:49:36 +000071/// `HashCode HashCode()` method on the `T` type, and will fallback to `std::hash<T>` if
Ben Clayton4dcbdda2023-09-20 14:51:34 +000072/// `T::HashCode` is missing.
Ryan Harrisondbc13af2022-02-21 15:19:07 +000073template <typename T>
Ben Claytone3f20052022-08-23 16:10:35 +000074struct Hasher {
75 /// @param value the value to hash
76 /// @returns a hash of the value
Ben Clayton76aec252024-02-05 20:49:36 +000077 HashCode operator()(const T& value) const {
Ben Clayton4dcbdda2023-09-20 14:51:34 +000078 if constexpr (detail::HasHashCodeMember<T>::value) {
Ben Claytonde9f5232023-11-13 13:46:36 +000079 auto hash = value.HashCode();
Ben Clayton76aec252024-02-05 20:49:36 +000080 static_assert(std::is_same_v<decltype(hash), HashCode>,
81 "T::HashCode() must return HashCode");
Ben Claytonde9f5232023-11-13 13:46:36 +000082 return hash;
Ben Clayton4dcbdda2023-09-20 14:51:34 +000083 } else {
Ben Clayton76aec252024-02-05 20:49:36 +000084 return static_cast<HashCode>(std::hash<T>()(value));
Ben Clayton4dcbdda2023-09-20 14:51:34 +000085 }
86 }
Ben Claytone3f20052022-08-23 16:10:35 +000087};
88
89/// Hasher specialization for pointers
Ben Claytone3f20052022-08-23 16:10:35 +000090template <typename T>
91struct Hasher<T*> {
92 /// @param ptr the pointer to hash
93 /// @returns a hash of the pointer
Ben Clayton76aec252024-02-05 20:49:36 +000094 HashCode operator()(T* ptr) const {
95 auto hash = reinterpret_cast<uintptr_t>(ptr);
Ben Claytonf2b86aa2022-12-13 14:46:02 +000096#ifdef TINT_HASH_SEED
97 hash ^= static_cast<uint32_t>(TINT_HASH_SEED);
98#endif
Ben Clayton76aec252024-02-05 20:49:36 +000099 if constexpr (sizeof(hash) > 4) {
100 return static_cast<HashCode>(hash >> 4 | hash >> 32);
101 } else {
102 return static_cast<HashCode>(hash >> 4);
103 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000104 }
Ben Claytone3f20052022-08-23 16:10:35 +0000105};
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000106
Ben Claytone3f20052022-08-23 16:10:35 +0000107/// Hasher specialization for std::vector
108template <typename T>
109struct Hasher<std::vector<T>> {
110 /// @param vector the vector to hash
111 /// @returns a hash of the vector
Ben Clayton76aec252024-02-05 20:49:36 +0000112 HashCode operator()(const std::vector<T>& vector) const {
Ben Claytone3f20052022-08-23 16:10:35 +0000113 auto hash = Hash(vector.size());
114 for (auto& el : vector) {
115 hash = HashCombine(hash, el);
116 }
117 return hash;
118 }
119};
120
Ben Claytone3f20052022-08-23 16:10:35 +0000121/// Hasher specialization for std::tuple
Ben Claytonb0664682022-05-19 17:59:19 +0000122template <typename... TYPES>
Ben Claytone3f20052022-08-23 16:10:35 +0000123struct Hasher<std::tuple<TYPES...>> {
124 /// @param tuple the tuple to hash
125 /// @returns a hash of the tuple
Ben Clayton76aec252024-02-05 20:49:36 +0000126 HashCode operator()(const std::tuple<TYPES...>& tuple) const {
Ben Claytone3f20052022-08-23 16:10:35 +0000127 return std::apply(Hash<TYPES...>, tuple);
128 }
129};
Ben Claytonb0664682022-05-19 17:59:19 +0000130
Ben Clayton7883a0c2023-04-20 23:51:53 +0000131/// Hasher specialization for std::pair
132template <typename A, typename B>
133struct Hasher<std::pair<A, B>> {
134 /// @param tuple the tuple to hash
135 /// @returns a hash of the tuple
Ben Clayton76aec252024-02-05 20:49:36 +0000136 HashCode operator()(const std::pair<A, B>& tuple) const {
137 return std::apply(Hash<A, B>, tuple);
138 }
Ben Clayton7883a0c2023-04-20 23:51:53 +0000139};
140
141/// Hasher specialization for std::variant
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000142template <typename... TYPES>
143struct Hasher<std::variant<TYPES...>> {
144 /// @param variant the variant to hash
145 /// @returns a hash of the tuple
Ben Clayton76aec252024-02-05 20:49:36 +0000146 HashCode operator()(const std::variant<TYPES...>& variant) const {
Ben Clayton0c7c23b2022-08-31 23:51:48 +0000147 return std::visit([](auto&& val) { return Hash(val); }, variant);
148 }
149};
150
Ben Claytonb703afc2023-05-05 18:55:15 +0000151/// Hasher specialization for std::string, which also supports hashing of const char* and
152/// std::string_view without first constructing a std::string.
153template <>
154struct Hasher<std::string> {
155 /// @param str the string to hash
156 /// @returns a hash of the string
Ben Clayton76aec252024-02-05 20:49:36 +0000157 HashCode operator()(const std::string& str) const {
158 return static_cast<HashCode>(std::hash<std::string_view>()(std::string_view(str)));
Ben Claytonb703afc2023-05-05 18:55:15 +0000159 }
160
161 /// @param str the string to hash
162 /// @returns a hash of the string
Ben Clayton76aec252024-02-05 20:49:36 +0000163 HashCode operator()(const char* str) const {
164 return static_cast<HashCode>(std::hash<std::string_view>()(std::string_view(str)));
Ben Claytonb703afc2023-05-05 18:55:15 +0000165 }
166
167 /// @param str the string to hash
168 /// @returns a hash of the string
Ben Clayton76aec252024-02-05 20:49:36 +0000169 HashCode operator()(const std::string_view& str) const {
170 return static_cast<HashCode>(std::hash<std::string_view>()(str));
Ben Claytonb703afc2023-05-05 18:55:15 +0000171 }
172};
173
Ben Clayton0740cf82023-07-28 21:40:41 +0000174/// @param args the arguments to hash
Ben Claytone3f20052022-08-23 16:10:35 +0000175/// @returns a hash of the variadic list of arguments.
176/// The returned hash is dependent on the order of the arguments.
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000177template <typename... ARGS>
Ben Clayton76aec252024-02-05 20:49:36 +0000178HashCode Hash(const ARGS&... args) {
Ben Claytone3f20052022-08-23 16:10:35 +0000179 if constexpr (sizeof...(ARGS) == 0) {
180 return 0;
181 } else if constexpr (sizeof...(ARGS) == 1) {
182 using T = std::tuple_element_t<0, std::tuple<ARGS...>>;
183 return Hasher<T>()(args...);
184 } else {
Ben Clayton76aec252024-02-05 20:49:36 +0000185 HashCode hash = 102931; // seed with an arbitrary prime
Ben Claytone3f20052022-08-23 16:10:35 +0000186 return HashCombine(hash, args...);
187 }
188}
189
Ben Clayton0740cf82023-07-28 21:40:41 +0000190/// @param hash the hash value to combine with
191/// @param values the values to hash
Ben Claytone3f20052022-08-23 16:10:35 +0000192/// @returns a hash of the variadic list of arguments.
193/// The returned hash is dependent on the order of the arguments.
194template <typename... ARGS>
Ben Clayton76aec252024-02-05 20:49:36 +0000195HashCode HashCombine(HashCode hash, const ARGS&... values) {
196#ifdef TINT_HASH_SEED
197 constexpr uint32_t offset = 0x7f4a7c16 ^ static_cast<uint32_t>(TINT_HASH_SEED);
198#else
199 constexpr uint32_t offset = 0x7f4a7c16;
200#endif
201
Ben Clayton51d88eb2022-12-13 16:53:31 +0000202 ((hash ^= Hash(values) + (offset ^ (hash >> 2))), ...);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000203 return hash;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000204}
205
Ben Claytonb703afc2023-05-05 18:55:15 +0000206/// A STL-compatible equal_to implementation that specializes for types.
207template <typename T>
208struct EqualTo {
209 /// @param lhs the left hand side value
210 /// @param rhs the right hand side value
211 /// @returns true if the two values are equal
212 constexpr bool operator()(const T& lhs, const T& rhs) const {
213 return std::equal_to<T>()(lhs, rhs);
214 }
215};
216
217/// A specialization for EqualTo for std::string, which supports additional comparision with
218/// std::string_view and const char*.
219template <>
220struct EqualTo<std::string> {
221 /// @param lhs the left hand side value
222 /// @param rhs the right hand side value
223 /// @returns true if the two values are equal
224 bool operator()(const std::string& lhs, const std::string& rhs) const { return lhs == rhs; }
225
226 /// @param lhs the left hand side value
227 /// @param rhs the right hand side value
228 /// @returns true if the two values are equal
229 bool operator()(const std::string& lhs, const char* rhs) const { return lhs == rhs; }
230
231 /// @param lhs the left hand side value
232 /// @param rhs the right hand side value
233 /// @returns true if the two values are equal
234 bool operator()(const std::string& lhs, std::string_view rhs) const { return lhs == rhs; }
235
236 /// @param lhs the left hand side value
237 /// @param rhs the right hand side value
238 /// @returns true if the two values are equal
239 bool operator()(const char* lhs, const std::string& rhs) const { return lhs == rhs; }
240
241 /// @param lhs the left hand side value
242 /// @param rhs the right hand side value
243 /// @returns true if the two values are equal
244 bool operator()(std::string_view lhs, const std::string& rhs) const { return lhs == rhs; }
245};
246
Ben Clayton046abc02022-04-28 13:08:22 +0000247/// Wrapper for a hashable type enabling the wrapped value to be used as a key
248/// for an unordered_map or unordered_set.
249template <typename T>
250struct UnorderedKeyWrapper {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000251 /// The wrapped value
Ben Clayton94181522022-11-09 20:55:33 +0000252 T value;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000253 /// The hash of value
Ben Clayton76aec252024-02-05 20:49:36 +0000254 HashCode hash;
Ben Clayton046abc02022-04-28 13:08:22 +0000255
dan sinclair41e4d9a2022-05-01 14:40:55 +0000256 /// Constructor
257 /// @param v the value to wrap
258 explicit UnorderedKeyWrapper(const T& v) : value(v), hash(Hash(v)) {}
Ben Clayton046abc02022-04-28 13:08:22 +0000259
dan sinclair41e4d9a2022-05-01 14:40:55 +0000260 /// Move constructor
261 /// @param v the value to wrap
262 explicit UnorderedKeyWrapper(T&& v) : value(std::move(v)), hash(Hash(value)) {}
Ben Clayton046abc02022-04-28 13:08:22 +0000263
dan sinclair41e4d9a2022-05-01 14:40:55 +0000264 /// @returns true if this wrapper comes before other
265 /// @param other the RHS of the operator
266 bool operator<(const UnorderedKeyWrapper& other) const { return hash < other.hash; }
Ben Clayton046abc02022-04-28 13:08:22 +0000267
dan sinclair41e4d9a2022-05-01 14:40:55 +0000268 /// @returns true if this wrapped value is equal to the other wrapped value
269 /// @param other the RHS of the operator
270 bool operator==(const UnorderedKeyWrapper& other) const { return value == other.value; }
Ben Clayton046abc02022-04-28 13:08:22 +0000271};
272
dan sinclairbae54e72023-07-28 15:01:54 +0000273} // namespace tint
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000274
Ben Clayton046abc02022-04-28 13:08:22 +0000275namespace std {
276
dan sinclairbae54e72023-07-28 15:01:54 +0000277/// Custom std::hash specialization for tint::UnorderedKeyWrapper
Ben Clayton046abc02022-04-28 13:08:22 +0000278template <typename T>
dan sinclairbae54e72023-07-28 15:01:54 +0000279class hash<tint::UnorderedKeyWrapper<T>> {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000280 public:
281 /// @param w the UnorderedKeyWrapper
282 /// @return the hash value
dan sinclairbae54e72023-07-28 15:01:54 +0000283 inline std::size_t operator()(const tint::UnorderedKeyWrapper<T>& w) const { return w.hash; }
Ben Clayton046abc02022-04-28 13:08:22 +0000284};
285
286} // namespace std
287
dan sinclair22b4dd22023-07-21 00:40:07 +0000288#endif // SRC_TINT_UTILS_MATH_HASH_H_