diff --git a/include/ircd/spirit.h b/include/ircd/spirit.h index e08d71713..20d6438a3 100644 --- a/include/ircd/spirit.h +++ b/include/ircd/spirit.h @@ -69,7 +69,8 @@ __attribute__((visibility("default"))) class... args> bool parse(args&&...); - template bool generate(mutable_buffer &out, gen&&, attr&&...); } @@ -265,7 +266,8 @@ struct ircd::spirit::generator_state ssize_t overflow {0}; }; -template inline bool ircd::generate(mutable_buffer &out, @@ -276,16 +278,8 @@ ircd::generate(mutable_buffer &out, using namespace ircd::spirit; namespace spirit = ircd::spirit; - const size_t max - { - size(out) - }; - - sink_type sink - { - begin(out) - }; - + const auto max(size(out)); + const auto start(data(out)); struct spirit::generator_state state { out @@ -296,20 +290,38 @@ ircd::generate(mutable_buffer &out, spirit::generator_state, &state }; - const bool ret + sink_type sink + { + begin(out) + }; + + const auto ret { karma::generate(sink, std::forward(g), std::forward(a)...) }; - if(unlikely(state.overflow)) + if constexpr(truncation) + { + begin(out) = state.overflow? end(out) : begin(out); + assert(!state.overflow || begin(out) == end(out)); + assert(begin(out) <= end(out)); + return ret; + } + + if(unlikely(state.overflow || begin(out) > end(out))) { char pbuf[2][48]; - begin(out) = end(out) - max; + const auto required + { + max + state.overflow?: + std::distance(start, begin(out)) + }; + throw spirit::buffer_overrun { - "Insufficient buffer of %s for %s", + "Insufficient buffer of %s; required at least %s", pretty(pbuf[0], iec(max)), - pretty(pbuf[1], iec(max + state.overflow)), + pretty(pbuf[1], iec(required)), }; } @@ -402,6 +414,7 @@ boost::spirit::karma::detail::buffer_sink::output(const char &value) ircd::consume(state.out, ircd::copy(state.out, value)) }; + assert(consumed <= 1UL); this->width += consumed; state.overflow += !consumed; state.generated++; diff --git a/ircd/fmt.cc b/ircd/fmt.cc index 51ae3cc99..42b93ae63 100644 --- a/ircd/fmt.cc +++ b/ircd/fmt.cc @@ -42,6 +42,7 @@ __attribute__((visibility("hidden"))) void handle_specifier(mutable_buffer &out, const uint &idx, const spec &, const arg &); template bool generate_string(char *&out, const size_t &max, generator&&, const arg &val); template bool visit_type(const arg &val, lambda&& closure); + template bool generate(mutable_buffer &, gen&&, attr&&...); }} /// Structural representation of a format specifier. The parse of each @@ -544,6 +545,21 @@ ircd::fmt::visit_type(const arg &val, return type == typeid(T)? closure(*static_cast(ptr)) : false; } +template +bool +ircd::fmt::generate(mutable_buffer &out, + gen&& g, + attr&&... a) +{ + constexpr bool truncation + { + true + }; + + return ircd::generate(out, std::forward(g), std::forward(a)...); +} + bool ircd::fmt::pointer_specifier::operator()(char *&out, const size_t &max, @@ -612,15 +628,15 @@ const }; if(!spec.width) - ret = generate(buf, generator | ep, uintptr_t(p)); + ret = fmt::generate(buf, generator | ep, uintptr_t(p)); else if(spec.sign == '-') { const auto &g(generator.aligned_left(spec.width, spec.pad)); - ret = generate(buf, g | ep, uintptr_t(p)); + ret = fmt::generate(buf, g | ep, uintptr_t(p)); } else { const auto &g(generator.aligned_right(spec.width, spec.pad)); - ret = generate(buf, g | ep, uintptr_t(p)); + ret = fmt::generate(buf, g | ep, uintptr_t(p)); } out = data(buf); @@ -667,7 +683,7 @@ const }; const auto &c(*static_cast(ptr)); - generate(buf, generator | eps[throw_illegal], c); + fmt::generate(buf, generator | eps[throw_illegal], c); out = data(buf); return true; } @@ -713,7 +729,7 @@ const const auto ret { - generate(buf, generator | eps[throw_illegal], boolean) + fmt::generate(buf, generator | eps[throw_illegal], boolean) }; out = data(buf); @@ -791,15 +807,15 @@ const }; if(!spec.width) - ret = generate(buf, generator | ep, integer); + ret = fmt::generate(buf, generator | ep, integer); else if(spec.sign == '-') { const auto &g(generator.aligned_left(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } else { const auto &g(generator.aligned_right(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } out = data(buf); @@ -877,15 +893,15 @@ const }; if(!spec.width) - ret = generate(buf, generator | ep, integer); + ret = fmt::generate(buf, generator | ep, integer); else if(spec.sign == '-') { const auto &g(generator.aligned_left(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } else { const auto &g(generator.aligned_right(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } out = data(buf); @@ -960,15 +976,15 @@ const }; if(!spec.width) - ret = generate(buf, generator | ep, integer); + ret = fmt::generate(buf, generator | ep, integer); else if(spec.sign == '-') { const auto &g(generator.aligned_left(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } else { const auto &g(generator.aligned_right(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } out = data(buf); @@ -1043,15 +1059,15 @@ const }; if(!spec.width) - ret = generate(buf, generator | ep, integer); + ret = fmt::generate(buf, generator | ep, integer); else if(spec.sign == '-') { const auto &g(generator.aligned_left(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } else { const auto &g(generator.aligned_right(spec.width, spec.pad)); - ret = generate(buf, g | ep, integer); + ret = fmt::generate(buf, g | ep, integer); } out = data(buf); @@ -1122,7 +1138,7 @@ const const auto ret { - generate(buf, generator | eps[throw_illegal], floating) + fmt::generate(buf, generator | eps[throw_illegal], floating) }; out = data(buf); @@ -1228,29 +1244,29 @@ ircd::fmt::generate_string(char *&out, type == typeid(ircd::json::array)) { const auto &str(*static_cast(ptr)); - ret = generate(buf, std::forward(gen), str); + ret = fmt::generate(buf, std::forward(gen), str); } else if(type == typeid(std::string_view)) { const auto &str(*static_cast(ptr)); - ret = generate(buf, std::forward(gen), str); + ret = fmt::generate(buf, std::forward(gen), str); } else if(type == typeid(std::string)) { const auto &str(*static_cast(ptr)); - ret = generate(buf, std::forward(gen), string_view{str}); + ret = fmt::generate(buf, std::forward(gen), string_view{str}); } else if(type == typeid(const char *)) { const char *const &str{*static_cast(ptr)}; - ret = generate(buf, std::forward(gen), string_view{str}); + ret = fmt::generate(buf, std::forward(gen), string_view{str}); } else { // This for string literals which have unique array types depending on their size. // There is no reasonable way to match them. The best that can be hoped for is the // grammar will fail gracefully (most of the time) or not print something bogus when // it happens to be legal. const auto &str(static_cast(ptr)); - ret = generate(buf, std::forward(gen), string_view{str}); + ret = fmt::generate(buf, std::forward(gen), string_view{str}); } out = data(buf);