hash_table.hpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*!
  2. @file
  3. Defines `boost::hana::detail::hash_table`.
  4. @copyright Louis Dionne 2016
  5. @copyright Jason Rice 2016
  6. Distributed under the Boost Software License, Version 1.0.
  7. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
  8. */
  9. #ifndef BOOST_HANA_DETAIL_HASH_TABLE_HPP
  10. #define BOOST_HANA_DETAIL_HASH_TABLE_HPP
  11. #include <boost/hana/equal.hpp>
  12. #include <boost/hana/ext/std/integer_sequence.hpp>
  13. #include <boost/hana/ext/std/integral_constant.hpp>
  14. #include <boost/hana/find_if.hpp>
  15. #include <boost/hana/fold_left.hpp>
  16. #include <boost/hana/hash.hpp>
  17. #include <boost/hana/optional.hpp>
  18. #include <boost/hana/range.hpp>
  19. #include <boost/hana/type.hpp>
  20. #include <boost/hana/value.hpp>
  21. #include <cstddef>
  22. #include <type_traits>
  23. #include <utility>
  24. BOOST_HANA_NAMESPACE_BEGIN namespace detail {
  25. template <typename Hash, std::size_t ...i>
  26. struct bucket { };
  27. template <typename ...Buckets>
  28. struct hash_table
  29. : Buckets...
  30. { };
  31. // find_indices:
  32. // Returns an `index_sequence` containing possible indices for the given
  33. // `Key` in the `Map`.
  34. template <typename Hash, std::size_t ...i>
  35. std::index_sequence<i...> find_indices_impl(bucket<Hash, i...> const&);
  36. template <typename Hash>
  37. std::index_sequence<> find_indices_impl(...);
  38. template <typename Map, typename Key>
  39. struct find_indices {
  40. using Hash = typename decltype(hana::hash(std::declval<Key>()))::type;
  41. using type = decltype(detail::find_indices_impl<Hash>(std::declval<Map>()));
  42. };
  43. // end find_indices
  44. // find_index:
  45. // Returns the actual index of a `Key` in the `Map`. The type of the key
  46. // associated to any given index must be retrievable with the `KeyAtIndex`
  47. // alias.
  48. template <template <std::size_t> class KeyAtIndex, typename Key>
  49. struct find_pred {
  50. template <typename Index>
  51. auto operator()(Index const&) const -> decltype(
  52. hana::equal(std::declval<KeyAtIndex<Index::value>>(),
  53. std::declval<Key>())
  54. );
  55. };
  56. template <typename Indices, typename Key, template <std::size_t> class KeyAtIndex>
  57. struct find_index_impl {
  58. using type = decltype(hana::find_if(Indices{}, find_pred<KeyAtIndex, Key>{}));
  59. };
  60. // This is a peephole optimization for buckets that have a single entry.
  61. // It provides a nice speedup in the at_key.number_of_lookups benchmark.
  62. // It is perhaps possible to make this part of `find_if` itself, but we
  63. // should make sure that we retain that speedup.
  64. template <std::size_t i, typename Key, template <std::size_t> class KeyAtIndex>
  65. struct find_index_impl<std::index_sequence<i>, Key, KeyAtIndex> {
  66. using Equal = decltype(
  67. hana::equal(std::declval<KeyAtIndex<i>>(),
  68. std::declval<Key>())
  69. );
  70. using type = typename std::conditional<Equal::value,
  71. hana::optional<std::integral_constant<std::size_t, i>>,
  72. hana::optional<>
  73. >::type;
  74. };
  75. template <typename Map, typename Key, template <std::size_t> class KeyAtIndex>
  76. struct find_index {
  77. using Indices = typename find_indices<Map, Key>::type;
  78. using type = typename find_index_impl<Indices, Key, KeyAtIndex>::type;
  79. };
  80. // end find_index
  81. // bucket_insert:
  82. // Inserts the given `Index` into the bucket of the `Map` in which `Key` falls.
  83. template <typename Bucket, typename Hash, std::size_t Index>
  84. struct update_bucket {
  85. using type = Bucket;
  86. };
  87. template <std::size_t ...i, typename Hash, std::size_t Index>
  88. struct update_bucket<bucket<Hash, i...>, Hash, Index> {
  89. using type = bucket<Hash, i..., Index>;
  90. };
  91. template <typename Map, typename Key, std::size_t Index, bool =
  92. (find_indices<Map, Key>::type::size() > 0)
  93. >
  94. struct bucket_insert;
  95. template <typename ...Buckets, typename Key, std::size_t Index>
  96. struct bucket_insert<hash_table<Buckets...>, Key, Index, true> {
  97. // There is a bucket for that Hash; append the new index to it.
  98. using Hash = typename decltype(hana::hash(std::declval<Key>()))::type;
  99. using type = hash_table<typename update_bucket<Buckets, Hash, Index>::type...>;
  100. };
  101. template <typename ...Buckets, typename Key, std::size_t Index>
  102. struct bucket_insert<hash_table<Buckets...>, Key, Index, false> {
  103. // There is no bucket for that Hash; insert a new bucket.
  104. using Hash = typename decltype(hana::hash(std::declval<Key>()))::type;
  105. using type = hash_table<Buckets..., bucket<Hash, Index>>;
  106. };
  107. // end bucket_insert
  108. // make_hash_table:
  109. // Creates a `hash_table` type able of holding the given number of
  110. // elements. The type of the key associated to any given index must
  111. // be retrievable using the `KeyAtIndex` alias. All the keys must
  112. // be distinct and have different hashes too.
  113. template <template <std::size_t> class KeyAtIndex, std::size_t N,
  114. typename Indices = std::make_index_sequence<N>>
  115. struct make_hash_table;
  116. template <template <std::size_t> class KeyAtIndex, std::size_t N, std::size_t ...i>
  117. struct make_hash_table<KeyAtIndex, N, std::index_sequence<i...>> {
  118. using type = hash_table<
  119. bucket<typename decltype(hana::hash(std::declval<KeyAtIndex<i>>()))::type, i>...
  120. >;
  121. };
  122. } BOOST_HANA_NAMESPACE_END
  123. #endif // !BOOST_HANA_DETAIL_HASH_TABLE_HPP