// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #ifndef HAVE_IRCD_SPIRIT_H #define HAVE_IRCD_SPIRIT_H /// This file is not part of the IRCd standard include list (stdinc.h) because /// it involves extremely expensive boost headers for creating formal spirit /// grammars. Include this in a definition file which defines such grammars. /// /// Note that directly sharing elements of a grammar between two compilation /// units can be achieved with forward declarations in `ircd/grammar.h`. // ircd.h is included here so that it can be compiled into this header. Then // this becomes the single leading precompiled header. #include // Disables asserts in spirit headers even when we're NDEBUG due to // some false asserts around boolean character tests in spirit. #define BOOST_DISABLE_ASSERTS // This prevents spirit grammar rules from generating a very large and/or deep // call-graph where rules compose together using wrapped indirect calls through // boost::function -- this is higly inefficient as the grammar's logic ends up // being a fraction of the generated code and the rest is invocation related // overhead. By force-flattening here we can allow each entry-point into // spirit to compose rules at once and eliminate the wrapping complex. #pragma clang attribute push ([[gnu::always_inline]], apply_to = function) #pragma clang attribute push ([[gnu::flatten]], apply_to = function) #include #include #pragma GCC visibility push (internal) #include #include #include #include #include #include #include #include #include #include #include #pragma GCC visibility pop #pragma clang attribute pop #pragma clang attribute pop namespace ircd { namespace spirit __attribute__((visibility("default"))) { template struct expectation_failure; IRCD_EXCEPTION(ircd::error, error); IRCD_EXCEPTION(error, generator_error); IRCD_EXCEPTION(generator_error, buffer_overrun); }} namespace ircd { namespace spirit __attribute__((visibility("internal"))) { namespace phx = boost::phoenix; namespace fusion = boost::fusion; namespace spirit = boost::spirit; namespace ascii = spirit::ascii; namespace karma = spirit::karma; namespace qi = spirit::qi; using _val_type = phx::actor>; using _r0_type = phx::actor>; using _r1_type = phx::actor>; using _r2_type = phx::actor>; using _r3_type = phx::actor>; using spirit::unused_type; using spirit::auto_; using spirit::_pass; using spirit::_val; using qi::locals; using qi::_a; using qi::_a_type; using qi::_r1_type; using qi::raw; using qi::omit; using qi::matches; using qi::hold; using qi::eoi; using qi::eps; using qi::expect; using qi::attr; using qi::attr_cast; using qi::repeat; using qi::lit; using qi::char_; using qi::byte_; using qi::string; using qi::short_; using qi::ushort_; using qi::word; using qi::big_word; using qi::little_word; using qi::int_; using qi::uint_; using qi::dword; using qi::big_dword; using qi::little_dword; using qi::long_; using qi::ulong_; using qi::qword; using qi::big_qword; using qi::little_qword; using qi::float_; using qi::bin_float; using qi::big_bin_float; using qi::little_bin_float; using qi::double_; using qi::bin_double; using qi::big_bin_double; using qi::little_bin_double; using spirit::repository::qi::seek; using karma::lit; using karma::char_; using karma::long_; using karma::double_; using karma::bool_; using karma::eps; using karma::attr_cast; using karma::maxwidth; using karma::buffer; using karma::skip; using prop_mask = mpl_::int_ < karma::generator_properties::no_properties | karma::generator_properties::buffering | karma::generator_properties::counting | karma::generator_properties::tracking | karma::generator_properties::disabling >; using sink_type = karma::detail::output_iterator; template auto & attr_at(semantic_context&&); template auto & local_at(semantic_context&&); }} namespace ircd::spirit::local { using qi::_0; using qi::_1; using qi::_2; using qi::_3; } namespace ircd { namespace spirit __attribute__((visibility("default"))) { // parse.cc extern thread_local char rule_buffer[64]; extern thread_local char generator_buffer[8][64_KiB]; extern thread_local struct generator_state *generator_state; }} namespace ircd { namespace spirit __attribute__((visibility("internal"))) { struct substring_view; struct custom_parser; BOOST_SPIRIT_TERMINAL(custom); template bool parse(const char *&start, const char *const &stop, gen&&, attr&&...); template bool parse(const char *&start, const char *const &stop, gen&&, attr&&...); template bool generate(mutable_buffer &out, gen&&, attr&&...); }} namespace ircd { using spirit::generate; using spirit::parse; } namespace boost { namespace spirit __attribute__((visibility("internal"))) { namespace qi { template struct make_primitive; } template<> struct use_terminal :mpl::true_ {}; }} struct [[gnu::visibility("internal")]] ircd::spirit::custom_parser :qi::primitive_parser { template struct attribute { using type = iterator; }; template boost::spirit::info what(context &) const { return boost::spirit::info("custom"); } template bool parse(iterator &, const iterator &, context &, const skipper &, attr &) const; }; template struct [[gnu::visibility("internal")]] boost::spirit::qi::make_primitive { using result_type = ircd::spirit::custom_parser; result_type operator()(unused_type, unused_type) const { return result_type{}; } }; struct ircd::spirit::substring_view :ircd::string_view { using _iterator = boost::spirit::karma::detail::indirect_iterator; using _iterator_range = boost::iterator_range<_iterator>; using ircd::string_view::string_view; explicit substring_view(const _iterator_range &range) :ircd::string_view { std::addressof(*range.begin()), std::addressof(*range.end()) } {} }; template struct __attribute__((visibility("default"))) ircd::spirit::expectation_failure :parent_error { template expectation_failure(const qi::expectation_failure &e, const ssize_t &show_max = 64); template expectation_failure(const qi::expectation_failure &e, const it &start, const ssize_t &show_max = 64); }; template template ircd::spirit::expectation_failure::expectation_failure(const qi::expectation_failure &e, const ssize_t &show_max) :parent { "Expected %s. You input %zd invalid characters :%s", ircd::string(rule_buffer, e.what_), std::distance(e.first, e.last), string_view{e.first, e.first + std::min(std::distance(e.first, e.last), show_max)} } {} template template ircd::spirit::expectation_failure::expectation_failure(const qi::expectation_failure &e, const it &start, const ssize_t &show_max) :parent { "Expected %s. You input %zd invalid characters somewhere between position %zd and %zd :%s", ircd::string(rule_buffer, e.what_), std::distance(e.first, e.last), std::distance(start, e.first), std::distance(start, e.last), string_view{e.first, e.first + std::min(std::distance(e.first, e.last), show_max)} } {} struct [[gnu::visibility("hidden")]] ircd::spirit::generator_state { /// The number of instances stacked behind the current state static uint depth() noexcept; /// User's buffer. mutable_buffer &out; /// Previous in buffer stack struct generator_state *prev {nullptr}; /// The number of characters we're storing in buffer uint consumed {0}; /// The number of characters attempted, which may be larger than /// the buffer capacity indicating an overflow amount. uint generated {0}; }; inline uint ircd::spirit::generator_state::depth() noexcept { uint ret(0); for(auto p(ircd::spirit::generator_state); p; p = p->prev) ++ret; return ret; } template [[using gnu: always_inline, gnu_inline]] extern inline bool ircd::spirit::generate(mutable_buffer &out, gen&& g, attr&&... a) { const auto max(size(out)); const auto start(data(out)); struct generator_state state { out }; const scope_restore _state { generator_state, &state }; sink_type sink { begin(out) }; const auto ret { karma::generate(sink, std::forward(g), std::forward(a)...) }; assert(state.generated >= state.consumed); const auto overflow { state.generated - state.consumed }; if constexpr(truncation) { begin(out) = begin(out) > end(out)? end(out): begin(out); assert(begin(out) <= end(out)); return ret; } if(unlikely(overflow || begin(out) > end(out))) { begin(out) = start; assert(begin(out) <= end(out)); char pbuf[2][48]; throw buffer_overrun { "Insufficient buffer of %s; required at least %s", pretty(pbuf[0], iec(state.consumed)), pretty(pbuf[1], iec(state.generated)), }; } assert(begin(out) >= end(out) - max); assert(begin(out) <= end(out)); return ret; } template [[using gnu: always_inline, gnu_inline, artificial]] extern inline bool ircd::spirit::parse(const char *&start, const char *const &stop, gen&& g, attr&&... a) try { return qi::parse(start, stop, std::forward(g), std::forward(a)...); } catch(const qi::expectation_failure &e) { throw expectation_failure { e, start, error_show_max }; } template [[using gnu: always_inline, gnu_inline, artificial]] extern inline bool ircd::spirit::parse(const char *&start, const char *const &stop, gen&& g, attr&&... a) { return qi::parse(start, stop, std::forward(g), std::forward(a)...); } template inline auto & ircd::spirit::local_at(semantic_context&& c) { return boost::fusion::at_c(c.locals); } template inline auto & ircd::spirit::attr_at(semantic_context&& c) { return boost::fusion::at_c(c.attributes); } // // boost::spirit::karma // namespace boost::spirit::karma::detail { template<> struct enable_buffering; } template<> struct [[gnu::visibility("internal")]] boost::spirit::karma::detail::enable_buffering { uint width { 0 }; uint depth { ircd::spirit::generator_state::depth() }; ircd::mutable_buffer buffer { depth? ircd::spirit::generator_buffer[depth - 1]: data(ircd::spirit::generator_state->out), depth? std::min(width, uint(sizeof(ircd::spirit::generator_buffer[depth - 1]))): size(ircd::spirit::generator_state->out), }; struct ircd::spirit::generator_state state { buffer, ircd::spirit::generator_state }; ircd::scope_restore stack_state { ircd::spirit::generator_state, std::addressof(state) }; std::size_t buffer_size() const { return state.generated; } void disable() { const auto width { this->width != -1U? this->width: state.consumed }; assert(width >= state.consumed); const uint off { width - state.consumed }; const ircd::const_buffer src { state.out, state.consumed }; const ircd::mutable_buffer dst { state.out + off }; const auto moved { ircd::move(dst, src) }; assert(moved == state.consumed); state.consumed = 0; } bool buffer_copy(std::size_t maxwidth = std::size_t(-1)) { assert(state.prev); #if __has_builtin(__builtin_assume) __builtin_assume(state.prev != nullptr); #endif auto &prev { *state.prev }; const bool prev_base { prev.prev == nullptr }; const auto width { this->width != -1U? this->width: state.consumed }; const ircd::const_buffer src { state.out, std::max(state.consumed, width) }; const ircd::mutable_buffer dst { data(prev.out) - (prev_base? state.consumed: -prev.consumed), prev_base? state.consumed: (size(prev.out) - prev.consumed) }; const auto copied { ircd::copy(dst, src) }; prev.generated += state.generated; prev.consumed += copied; return true; // sink.good(); } template bool buffer_copy_to(OutputIterator& sink, std::size_t maxwidth = std::size_t(-1)) const { ircd::always_assert(false); return true; } template bool buffer_copy_rest(RestIterator& sink, std::size_t start_at = 0) const { ircd::always_assert(false); return true; } enable_buffering(ircd::spirit::sink_type &sink, std::size_t width = std::size_t(-1)) :width { uint(width) } { assert(size(buffer) != 0); assert(this->depth <= 8); } ~enable_buffering() noexcept { assert(ircd::spirit::generator_state == &state); //disable(); } }; template<> inline bool boost::spirit::karma::detail::buffering_policy::output(const char &value) { assert(ircd::spirit::generator_state); #if __has_builtin(__builtin_assume) __builtin_assume(ircd::spirit::generator_state != nullptr); #endif auto &state { *ircd::spirit::generator_state }; const bool buffering { this->buffer != nullptr }; const bool base { state.prev == nullptr }; const uint off { ircd::boolmask(!base | buffering) & state.consumed }; const auto dst { state.out + off }; const auto copied { ircd::copy(dst, value) }; state.consumed += copied; state.generated += sizeof(char); return !buffering; } template<> inline void boost::spirit::karma::detail::position_policy::output(const char &value) { } #if 0 template<> template<> inline void boost::spirit::karma::detail::counting_policy::output(const char &value) { } #endif template<> inline bool ircd::spirit::sink_type::good() const { return true; } #endif // HAVE_IRCD_SPIRIT_H