// The Construct // // Copyright (C) The Construct Developers, Authors & Contributors // Copyright (C) 2016-2020 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. #define HAVE_IRCD_SPIRIT_GENERATE_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. This file is automatically included in the spirit.h group. #ifdef __clang__ #define IRCD_SPIRIT_GSPTR_LINKAGE static #else #define IRCD_SPIRIT_GSPTR_LINKAGE extern #endif namespace ircd { namespace spirit __attribute__((visibility("default"))) { IRCD_EXCEPTION(error, generator_error); IRCD_EXCEPTION(generator_error, buffer_overrun); IRCD_SPIRIT_GSPTR_LINKAGE thread_local struct generator_state *generator_state; extern thread_local char generator_buffer[8][64_KiB]; }} namespace ircd { namespace spirit __attribute__((visibility("internal"))) { 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 bool generate(mutable_buffer &out, gen&&, attr&&...); }} 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}; }; 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; } 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; } // // 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? 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; } template bool buffer_copy_to(OutputIterator& sink, std::size_t maxwidth = std::size_t(-1)) const = delete; template bool buffer_copy_rest(RestIterator& sink, std::size_t start_at = 0) const = delete; bool buffer_copy(std::size_t maxwidth = std::size_t(-1)); void disable(); enable_buffering(ircd::spirit::sink_type &sink, std::size_t width = std::size_t(-1)); ~enable_buffering() noexcept; }; inline boost::spirit::karma::detail::enable_buffering::enable_buffering(ircd::spirit::sink_type &sink, std::size_t width) :width { uint(width) } { assert(size(buffer) != 0); assert(this->depth <= 8); } inline boost::spirit::karma::detail::enable_buffering::~enable_buffering() noexcept { assert(ircd::spirit::generator_state == &state); //disable(); } inline void boost::spirit::karma::detail::enable_buffering::disable() { const auto width { this->width != -1U? this->width: state.consumed }; const uint off { width > state.consumed? width - state.consumed: 0U }; 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 = !off? state.consumed: 0U; } inline bool boost::spirit::karma::detail::enable_buffering::buffer_copy(std::size_t maxwidth) { assert(state.prev); auto &prev { *state.prev }; const bool prev_base { prev.prev == nullptr }; const auto width { this->width != -1U? this->width: state.consumed }; const auto dst_disp { prev_base? -ssize_t(state.consumed): ssize_t(prev.consumed) }; const auto dst_over { std::begin(prev.out) > std::end(prev.out)? std::distance(std::end(prev.out), std::begin(prev.out)): 0L }; const auto dst_size { prev_base? std::max(state.consumed - dst_over, 0L): size(prev.out) - prev.consumed }; const ircd::mutable_buffer dst { data(prev.out) + dst_disp, dst_size }; const ircd::const_buffer src { state.out, std::max(state.consumed, width) }; const auto copied { ircd::copy(dst, src) }; prev.generated += state.generated; prev.consumed += copied; return true; // sink.good(); } template<> inline bool boost::spirit::karma::detail::buffering_policy::output(const char &value) { assert(ircd::spirit::generator_state); 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) { assert(count == nullptr); } #endif template<> template<> inline bool boost::spirit::karma::detail::disabling_output_iterator < boost::spirit::karma::detail::buffering_policy, boost::spirit::karma::detail::counting_policy, boost::spirit::karma::detail::position_policy > ::output(const char &value) { assert(do_output); this->counting_policy::output(value); this->tracking_policy::output(value); return this->buffering_policy::output(value); } template<> template<> inline void ircd::spirit::sink_type::operator=(const char &value) { this->base_iterator::output(value); } template<> inline ircd::spirit::sink_type & ircd::spirit::sink_type::operator++() { const bool has_buffer { this->base_iterator::has_buffer() }; (*sink) += !has_buffer; return *this; } template<> inline ircd::spirit::sink_type ircd::spirit::sink_type::operator++(int) { auto copy(*this); ++(*this); return copy; } template<> inline bool ircd::spirit::sink_type::good() const { return true; }