vinipsmaker

hana-gperf.cpp

May 24th, 2020
234
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.08 KB | None | 0 0
  1. // @author Vinícius dos Santos Oliveira
  2. // I'm releasing this snippet as public domain.
  3. // Do as you like.
  4.  
  5. #include <boost/hana/functional/overload_linearly.hpp>
  6. #include <boost/hana/functional/overload.hpp>
  7. #include <boost/hana/functional/always.hpp>
  8. #include <boost/hana/fold_right.hpp>
  9. #include <boost/hana/drop_back.hpp>
  10. #include <boost/hana/not_equal.hpp>
  11. #include <boost/hana/greater.hpp>
  12. #include <boost/hana/minimum.hpp>
  13. #include <boost/hana/reverse.hpp>
  14. #include <boost/hana/string.hpp>
  15. #include <boost/hana/minus.hpp>
  16. #include <boost/hana/range.hpp>
  17. #include <boost/hana/tuple.hpp>
  18. #include <boost/hana/fold.hpp>
  19. #include <boost/hana/mult.hpp>
  20. #include <boost/hana/size.hpp>
  21. #include <boost/hana/sort.hpp>
  22. #include <boost/hana/set.hpp>
  23. #include <boost/hana/zip.hpp>
  24.  
  25. #include <string_view>
  26. #include <iostream>
  27.  
  28. struct lua_State;
  29.  
  30. namespace hana = boost::hana;
  31.  
  32. namespace dispatch_table {
  33. namespace detail {
  34. using hana::literals::operator""_c;
  35.  
  36. // string_view compares string size before string contents. Therefore, string
  37. // size here already performs work similar to the hash table usage. On the event
  38. // where all string sizes differ, we don't generate another hash function.
  39. struct ConstantHash
  40. {
  41.     static constexpr auto value = hana::always(hana::size_c<0>);
  42.     static constexpr auto first_pass = hana::always(hana::true_c);
  43. };
  44.  
  45. template<class Strings>
  46. struct Hash
  47. {
  48.     static constexpr std::size_t smallest_pattern_size = hana::minimum(
  49.         hana::transform(Strings{}, hana::size));
  50.  
  51.     // We're after a cheap hash for our static search structure, not a perfect
  52.     // hash.
  53.     static constexpr auto value = []() {
  54.         // The algorithm to find the suitable indexes is roughly divided into
  55.         // these steps:
  56.         //
  57.         // * Produce indexes [0, N).
  58.         // * Produce reverse indexes [N, 1].
  59.         // * Whether and index is reversed is indicated by the boolean constant
  60.         //   that follows the index. `true` means reversed. Reverse index count
  61.         //   from the end iterator.
  62.         // * We use N=smallest_pattern_size because we don't want to perform
  63.         //   bounds checking later.
  64.         // * Concat the two sequences.
  65.         auto initial_idxs = hana::concat(
  66.             hana::transform(
  67.                 hana::to_tuple(
  68.                     hana::range_c<std::size_t, 0, smallest_pattern_size>),
  69.                 [](auto&& i) { return hana::make_tuple(i, hana::false_c); }
  70.             ),
  71.             hana::reverse(hana::transform(
  72.                 hana::to_tuple(
  73.                     hana::range_c<std::size_t, 1, smallest_pattern_size + 1>),
  74.                 [](auto&& i) { return hana::make_tuple(i, hana::true_c); }
  75.             ))
  76.         );
  77.         // * Apply all strings to each index. The application will compute the
  78.         //   hash value, store it into a set and append the set's length to the
  79.         //   end of the index tuple. This length indicates a score.
  80.         // * Remove all indexes whose score is 1. They can't be used to
  81.         //   differentiate between any two strings.
  82.         auto idxs_plus_score = initial_idxs |
  83.             [](auto&& e) {
  84.                 auto score = hana::size(hana::to_set(hana::transform(
  85.                     Strings{},
  86.                     [idx=e[0_c],rev=e[1_c]](auto&& str) {
  87.                         return hana::if_(
  88.                             rev, str[hana::size(str) - idx], str[idx]);
  89.                     }
  90.                 )));
  91.                 return hana::if_(
  92.                     score == hana::size_c<1>,
  93.                     hana::make_tuple(),
  94.                     hana::make_tuple(hana::insert(e, hana::int_c<2>, score)));
  95.             };
  96.         // * Apply a stable sort by decreasing score.
  97.         // * Limit the indexes list to length 4 (i.e. bytes in an int32_t).
  98.         // * Discard score.
  99.         auto uncompressed_hash = hana::transform(
  100.             hana::take_front_c<4>(hana::sort(
  101.                 idxs_plus_score,
  102.                 [](auto&& l, auto&& r) { return hana::greater(l[2_c], r[2_c]); }
  103.             )),
  104.             hana::take_front_c<2>
  105.         );
  106.         // * Consider the result as a single hash. Generate more hashes by
  107.         //   discarding tail until new permutations are no longer possible.
  108.         auto final_round_choices = hana::while_(
  109.             [](auto&& e) { return hana::size(e[0_c]) != hana::size_c<1>; },
  110.             hana::make_tuple(uncompressed_hash),
  111.             [](auto&& e) {
  112.                 return hana::insert(e, 0_c, hana::drop_back(e[0_c]));
  113.             }
  114.         );
  115.         // * Compute a score again by applying all the strings to each hash
  116.         //   candidate. But first we create the function that generate the hash
  117.         //   function out of the hash spec.
  118.         // * Sort by score plus hash complexity (i.e. all else being equal,
  119.         //   cheaper hashes stay at the front) and get the best one.
  120.         auto hash_gen = [](auto&& spec) {
  121.             return hana::fold(
  122.                 hana::zip(
  123.                     spec,
  124.                     hana::transform(
  125.                         hana::to_tuple(
  126.                             hana::make_range(hana::size_c<0>, hana::size(spec))
  127.                         ),
  128.                         hana::_ * hana::integral_c<std::uint32_t, 8>)),
  129.                 hana::always(hana::integral_c<std::uint32_t, 0>),
  130.                 [](auto&& acc, auto&& subhash) {
  131.                     return [acc,subhash](const auto& string) {
  132.                         auto size = hana::overload(
  133.                             [](std::string_view str) { return str.size(); },
  134.                             hana::size
  135.                         );
  136.                         auto at = hana::overload_linearly(
  137.                             [](std::string_view str, std::size_t i) {
  138.                                 return std::uint32_t(str[i]);
  139.                             },
  140.                             [](auto&& xs, auto&& i) {
  141.                                 return hana::to<
  142.                                     hana::integral_constant_tag<std::uint32_t>
  143.                                 >(hana::at(xs, i));
  144.                             }
  145.                         );
  146.                         auto idx = hana::if_(
  147.                             /*reverse_index=*/subhash[0_c][1_c],
  148.                             size(string) - subhash[0_c][0_c],
  149.                             subhash[0_c][0_c]);
  150.                         return acc(string) + (at(string, idx) << subhash[1_c]);
  151.                     };
  152.                 }
  153.             );
  154.         };
  155.         return hash_gen(/*spec=*/hana::first(hana::front(hana::sort(
  156.             hana::transform(
  157.                 final_round_choices,
  158.                 [hash_gen](auto&& e) {
  159.                     auto score = hana::size(hana::to_set(
  160.                         hana::transform(Strings{}, hash_gen(e))));
  161.                     return hana::make_pair(e, score);
  162.                 }),
  163.             [](auto&& l, auto&& r) {
  164.                 return hana::greater(hana::second(l), hana::second(r));
  165.             }
  166.         ))));
  167.     }();
  168.  
  169.     static constexpr bool first_pass(std::string_view str)
  170.     {
  171.         // So later code won't have to perform bounds checking when computing
  172.         // hash
  173.         if (str.size() < smallest_pattern_size)
  174.             return false;
  175.  
  176.         return true;
  177.     }
  178. };
  179.  
  180. template<class Xs>
  181. constexpr bool all_sizes_differ(const Xs& xs)
  182. {
  183.     return hana::size(hana::to_set(
  184.         hana::transform(hana::transform(xs, hana::first), hana::size)
  185.     )) == hana::size(xs);
  186. }
  187. } // namespace detail
  188.  
  189. // Even if a perfect-hash is generated, a walk through the whole table is
  190. // done. The rationale is to keep the generated code small. Also, for our
  191. // use-case, not-found is an exceptional case, so we purposefully don't optimize
  192. // for it.
  193. template<class Xs, class Fallback, class... Args>
  194. auto dispatch(Xs xs, Fallback fallback, std::string_view key, Args&&... args)
  195.     -> decltype(fallback(key, std::forward<Args>(args)...))
  196. {
  197.     std::conditional_t<
  198.         detail::all_sizes_differ(xs),
  199.         detail::ConstantHash,
  200.         detail::Hash<decltype(hana::transform(xs, hana::first))>
  201.     > hash;
  202.     if (!hash.first_pass(key))
  203.         return fallback(key, std::forward<Args>(args)...);
  204.  
  205.     return hana::fold_right(
  206.         xs,
  207.         fallback,
  208.         [hash](const auto& arm, const auto& acc) {
  209.             return [hash,arm,acc](std::string_view key, auto&&... args) {
  210.                 auto pattern_ct = hana::first(arm);
  211.                 std::string_view pattern{
  212.                     pattern_ct.c_str(), hana::size(pattern_ct)};
  213.                 if (hash.value(pattern_ct) == hash.value(key) &&
  214.                     pattern == key) {
  215.                     return hana::second(arm)(
  216.                         std::forward<decltype(args)>(args)...);
  217.                 } else {
  218.                     return acc(key, std::forward<decltype(args)>(args)...);
  219.                 }
  220.             };
  221.         }
  222.     )(key, std::forward<Args>(args)...);
  223. }
  224. } // namespace dispatch_table
  225.  
  226. int foobar(std::string_view key, lua_State* L)
  227. {
  228.     return dispatch_table::dispatch(
  229.         hana::make_tuple(
  230.             hana::make_pair(
  231.                 BOOST_HANA_STRING("joinable"),
  232.                 [](lua_State*) -> int { return 1; }
  233.             ),
  234.             hana::make_pair(
  235.                 BOOST_HANA_STRING("join"),
  236.                 [](lua_State*) -> int { return 2; }
  237.             ),
  238.             hana::make_pair(
  239.                 BOOST_HANA_STRING("detach"),
  240.                 [](lua_State*) -> int { return 3; }
  241.             ),
  242.             hana::make_pair(
  243.                 BOOST_HANA_STRING("interrupt"),
  244.                 [](lua_State*) -> int { return 4; }
  245.             ),
  246.             hana::make_pair(
  247.                 BOOST_HANA_STRING("interruption_caught"),
  248.                 [](lua_State*) -> int { return 5; }
  249.             )
  250.         ),
  251.         [](std::string_view /*key*/, lua_State*) -> int { return -1; },
  252.         key,
  253.         L
  254.     );
  255. }
  256.  
  257. int main()
  258. {
  259.     std::string input;
  260.     std::getline(std::cin, input);
  261.     std::cout << foobar(input, nullptr) << std::endl;
  262. }
Add Comment
Please, Sign In to add comment