// @author Vinícius dos Santos Oliveira // I'm releasing this snippet as public domain. // Do as you like. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct lua_State; namespace hana = boost::hana; namespace dispatch_table { namespace detail { using hana::literals::operator""_c; // string_view compares string size before string contents. Therefore, string // size here already performs work similar to the hash table usage. On the event // where all string sizes differ, we don't generate another hash function. struct ConstantHash { static constexpr auto value = hana::always(hana::size_c<0>); static constexpr auto first_pass = hana::always(hana::true_c); }; template struct Hash { static constexpr std::size_t smallest_pattern_size = hana::minimum( hana::transform(Strings{}, hana::size)); // We're after a cheap hash for our static search structure, not a perfect // hash. static constexpr auto value = []() { // The algorithm to find the suitable indexes is roughly divided into // these steps: // // * Produce indexes [0, N). // * Produce reverse indexes [N, 1]. // * Whether and index is reversed is indicated by the boolean constant // that follows the index. `true` means reversed. Reverse index count // from the end iterator. // * We use N=smallest_pattern_size because we don't want to perform // bounds checking later. // * Concat the two sequences. auto initial_idxs = hana::concat( hana::transform( hana::to_tuple( hana::range_c), [](auto&& i) { return hana::make_tuple(i, hana::false_c); } ), hana::reverse(hana::transform( hana::to_tuple( hana::range_c), [](auto&& i) { return hana::make_tuple(i, hana::true_c); } )) ); // * Apply all strings to each index. The application will compute the // hash value, store it into a set and append the set's length to the // end of the index tuple. This length indicates a score. // * Remove all indexes whose score is 1. They can't be used to // differentiate between any two strings. auto idxs_plus_score = initial_idxs | [](auto&& e) { auto score = hana::size(hana::to_set(hana::transform( Strings{}, [idx=e[0_c],rev=e[1_c]](auto&& str) { return hana::if_( rev, str[hana::size(str) - idx], str[idx]); } ))); return hana::if_( score == hana::size_c<1>, hana::make_tuple(), hana::make_tuple(hana::insert(e, hana::int_c<2>, score))); }; // * Apply a stable sort by decreasing score. // * Limit the indexes list to length 4 (i.e. bytes in an int32_t). // * Discard score. auto uncompressed_hash = hana::transform( hana::take_front_c<4>(hana::sort( idxs_plus_score, [](auto&& l, auto&& r) { return hana::greater(l[2_c], r[2_c]); } )), hana::take_front_c<2> ); // * Consider the result as a single hash. Generate more hashes by // discarding tail until new permutations are no longer possible. auto final_round_choices = hana::while_( [](auto&& e) { return hana::size(e[0_c]) != hana::size_c<1>; }, hana::make_tuple(uncompressed_hash), [](auto&& e) { return hana::insert(e, 0_c, hana::drop_back(e[0_c])); } ); // * Compute a score again by applying all the strings to each hash // candidate. But first we create the function that generate the hash // function out of the hash spec. // * Sort by score plus hash complexity (i.e. all else being equal, // cheaper hashes stay at the front) and get the best one. auto hash_gen = [](auto&& spec) { return hana::fold( hana::zip( spec, hana::transform( hana::to_tuple( hana::make_range(hana::size_c<0>, hana::size(spec)) ), hana::_ * hana::integral_c)), hana::always(hana::integral_c), [](auto&& acc, auto&& subhash) { return [acc,subhash](const auto& string) { auto size = hana::overload( [](std::string_view str) { return str.size(); }, hana::size ); auto at = hana::overload_linearly( [](std::string_view str, std::size_t i) { return std::uint32_t(str[i]); }, [](auto&& xs, auto&& i) { return hana::to< hana::integral_constant_tag >(hana::at(xs, i)); } ); auto idx = hana::if_( /*reverse_index=*/subhash[0_c][1_c], size(string) - subhash[0_c][0_c], subhash[0_c][0_c]); return acc(string) + (at(string, idx) << subhash[1_c]); }; } ); }; return hash_gen(/*spec=*/hana::first(hana::front(hana::sort( hana::transform( final_round_choices, [hash_gen](auto&& e) { auto score = hana::size(hana::to_set( hana::transform(Strings{}, hash_gen(e)))); return hana::make_pair(e, score); }), [](auto&& l, auto&& r) { return hana::greater(hana::second(l), hana::second(r)); } )))); }(); static constexpr bool first_pass(std::string_view str) { // So later code won't have to perform bounds checking when computing // hash if (str.size() < smallest_pattern_size) return false; return true; } }; template constexpr bool all_sizes_differ(const Xs& xs) { return hana::size(hana::to_set( hana::transform(hana::transform(xs, hana::first), hana::size) )) == hana::size(xs); } } // namespace detail // Even if a perfect-hash is generated, a walk through the whole table is // done. The rationale is to keep the generated code small. Also, for our // use-case, not-found is an exceptional case, so we purposefully don't optimize // for it. template auto dispatch(Xs xs, Fallback fallback, std::string_view key, Args&&... args) -> decltype(fallback(key, std::forward(args)...)) { std::conditional_t< detail::all_sizes_differ(xs), detail::ConstantHash, detail::Hash > hash; if (!hash.first_pass(key)) return fallback(key, std::forward(args)...); return hana::fold_right( xs, fallback, [hash](const auto& arm, const auto& acc) { return [hash,arm,acc](std::string_view key, auto&&... args) { auto pattern_ct = hana::first(arm); std::string_view pattern{ pattern_ct.c_str(), hana::size(pattern_ct)}; if (hash.value(pattern_ct) == hash.value(key) && pattern == key) { return hana::second(arm)( std::forward(args)...); } else { return acc(key, std::forward(args)...); } }; } )(key, std::forward(args)...); } } // namespace dispatch_table int foobar(std::string_view key, lua_State* L) { return dispatch_table::dispatch( hana::make_tuple( hana::make_pair( BOOST_HANA_STRING("joinable"), [](lua_State*) -> int { return 1; } ), hana::make_pair( BOOST_HANA_STRING("join"), [](lua_State*) -> int { return 2; } ), hana::make_pair( BOOST_HANA_STRING("detach"), [](lua_State*) -> int { return 3; } ), hana::make_pair( BOOST_HANA_STRING("interrupt"), [](lua_State*) -> int { return 4; } ), hana::make_pair( BOOST_HANA_STRING("interruption_caught"), [](lua_State*) -> int { return 5; } ) ), [](std::string_view /*key*/, lua_State*) -> int { return -1; }, key, L ); } int main() { std::string input; std::getline(std::cin, input); std::cout << foobar(input, nullptr) << std::endl; }