hash_policy_traits.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef Y_ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
  15. #define Y_ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
  16. #include <cstddef>
  17. #include <memory>
  18. #include <new>
  19. #include <type_traits>
  20. #include <utility>
  21. #include "y_absl/container/internal/common_policy_traits.h"
  22. #include "y_absl/meta/type_traits.h"
  23. namespace y_absl {
  24. Y_ABSL_NAMESPACE_BEGIN
  25. namespace container_internal {
  26. // Defines how slots are initialized/destroyed/moved.
  27. template <class Policy, class = void>
  28. struct hash_policy_traits : common_policy_traits<Policy> {
  29. // The type of the keys stored in the hashtable.
  30. using key_type = typename Policy::key_type;
  31. private:
  32. struct ReturnKey {
  33. // When C++17 is available, we can use std::launder to provide mutable
  34. // access to the key for use in node handle.
  35. #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
  36. template <class Key,
  37. y_absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0>
  38. static key_type& Impl(Key&& k, int) {
  39. return *std::launder(
  40. const_cast<key_type*>(std::addressof(std::forward<Key>(k))));
  41. }
  42. #endif
  43. template <class Key>
  44. static Key Impl(Key&& k, char) {
  45. return std::forward<Key>(k);
  46. }
  47. // When Key=T&, we forward the lvalue reference.
  48. // When Key=T, we return by value to avoid a dangling reference.
  49. // eg, for string_hash_map.
  50. template <class Key, class... Args>
  51. auto operator()(Key&& k, const Args&...) const
  52. -> decltype(Impl(std::forward<Key>(k), 0)) {
  53. return Impl(std::forward<Key>(k), 0);
  54. }
  55. };
  56. template <class P = Policy, class = void>
  57. struct ConstantIteratorsImpl : std::false_type {};
  58. template <class P>
  59. struct ConstantIteratorsImpl<P, y_absl::void_t<typename P::constant_iterators>>
  60. : P::constant_iterators {};
  61. public:
  62. // The actual object stored in the hash table.
  63. using slot_type = typename Policy::slot_type;
  64. // The argument type for insertions into the hashtable. This is different
  65. // from value_type for increased performance. See initializer_list constructor
  66. // and insert() member functions for more details.
  67. using init_type = typename Policy::init_type;
  68. using reference = decltype(Policy::element(std::declval<slot_type*>()));
  69. using pointer = typename std::remove_reference<reference>::type*;
  70. using value_type = typename std::remove_reference<reference>::type;
  71. // Policies can set this variable to tell raw_hash_set that all iterators
  72. // should be constant, even `iterator`. This is useful for set-like
  73. // containers.
  74. // Defaults to false if not provided by the policy.
  75. using constant_iterators = ConstantIteratorsImpl<>;
  76. // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`.
  77. //
  78. // If `slot` is nullptr, returns the constant amount of memory owned by any
  79. // full slot or -1 if slots own variable amounts of memory.
  80. //
  81. // PRECONDITION: `slot` is INITIALIZED or nullptr
  82. template <class P = Policy>
  83. static size_t space_used(const slot_type* slot) {
  84. return P::space_used(slot);
  85. }
  86. // Provides generalized access to the key for elements, both for elements in
  87. // the table and for elements that have not yet been inserted (or even
  88. // constructed). We would like an API that allows us to say: `key(args...)`
  89. // but we cannot do that for all cases, so we use this more general API that
  90. // can be used for many things, including the following:
  91. //
  92. // - Given an element in a table, get its key.
  93. // - Given an element initializer, get its key.
  94. // - Given `emplace()` arguments, get the element key.
  95. //
  96. // Implementations of this must adhere to a very strict technical
  97. // specification around aliasing and consuming arguments:
  98. //
  99. // Let `value_type` be the result type of `element()` without ref- and
  100. // cv-qualifiers. The first argument is a functor, the rest are constructor
  101. // arguments for `value_type`. Returns `std::forward<F>(f)(k, xs...)`, where
  102. // `k` is the element key, and `xs...` are the new constructor arguments for
  103. // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias
  104. // `ts...`. The key won't be touched once `xs...` are used to construct an
  105. // element; `ts...` won't be touched at all, which allows `apply()` to consume
  106. // any rvalues among them.
  107. //
  108. // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not
  109. // trigger a hard compile error unless it originates from `f`. In other words,
  110. // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not
  111. // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK.
  112. //
  113. // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`,
  114. // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not.
  115. template <class F, class... Ts, class P = Policy>
  116. static auto apply(F&& f, Ts&&... ts)
  117. -> decltype(P::apply(std::forward<F>(f), std::forward<Ts>(ts)...)) {
  118. return P::apply(std::forward<F>(f), std::forward<Ts>(ts)...);
  119. }
  120. // Returns the "key" portion of the slot.
  121. // Used for node handle manipulation.
  122. template <class P = Policy>
  123. static auto mutable_key(slot_type* slot)
  124. -> decltype(P::apply(ReturnKey(), hash_policy_traits::element(slot))) {
  125. return P::apply(ReturnKey(), hash_policy_traits::element(slot));
  126. }
  127. // Returns the "value" (as opposed to the "key") portion of the element. Used
  128. // by maps to implement `operator[]`, `at()` and `insert_or_assign()`.
  129. template <class T, class P = Policy>
  130. static auto value(T* elem) -> decltype(P::value(elem)) {
  131. return P::value(elem);
  132. }
  133. using HashSlotFn = size_t (*)(const void* hash_fn, void* slot);
  134. template <class Hash>
  135. static constexpr HashSlotFn get_hash_slot_fn() {
  136. // get_hash_slot_fn may return nullptr to signal that non type erased function
  137. // should be used. GCC warns against comparing function address with nullptr.
  138. #if defined(__GNUC__) && !defined(__clang__)
  139. #pragma GCC diagnostic push
  140. // silent error: the address of * will never be NULL [-Werror=address]
  141. #pragma GCC diagnostic ignored "-Waddress"
  142. #endif
  143. return Policy::template get_hash_slot_fn<Hash>() == nullptr
  144. ? &hash_slot_fn_non_type_erased<Hash>
  145. : Policy::template get_hash_slot_fn<Hash>();
  146. #if defined(__GNUC__) && !defined(__clang__)
  147. #pragma GCC diagnostic pop
  148. #endif
  149. }
  150. // Whether small object optimization is enabled. True by default.
  151. static constexpr bool soo_enabled() { return soo_enabled_impl(Rank1{}); }
  152. private:
  153. template <class Hash>
  154. struct HashElement {
  155. template <class K, class... Args>
  156. size_t operator()(const K& key, Args&&...) const {
  157. return h(key);
  158. }
  159. const Hash& h;
  160. };
  161. template <class Hash>
  162. static size_t hash_slot_fn_non_type_erased(const void* hash_fn, void* slot) {
  163. return Policy::apply(HashElement<Hash>{*static_cast<const Hash*>(hash_fn)},
  164. Policy::element(static_cast<slot_type*>(slot)));
  165. }
  166. // Use go/ranked-overloads for dispatching. Rank1 is preferred.
  167. struct Rank0 {};
  168. struct Rank1 : Rank0 {};
  169. // Use auto -> decltype as an enabler.
  170. template <class P = Policy>
  171. static constexpr auto soo_enabled_impl(Rank1) -> decltype(P::soo_enabled()) {
  172. return P::soo_enabled();
  173. }
  174. static constexpr bool soo_enabled_impl(Rank0) { return true; }
  175. };
  176. } // namespace container_internal
  177. Y_ABSL_NAMESPACE_END
  178. } // namespace y_absl
  179. #endif // Y_ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_