diff --git a/src/c4/yml/common.hpp b/src/c4/yml/common.hpp index f516f6cf..57048811 100644 --- a/src/c4/yml/common.hpp +++ b/src/c4/yml/common.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #ifdef C4_MSVC @@ -14,19 +15,37 @@ #endif + +//----------------------------------------------------------------------------- + +#ifndef RYML_ERRMSG_SIZE +/// size for the error message buffer +#define RYML_ERRMSG_SIZE (1024) +#endif + #ifndef RYML_LOGBUF_SIZE -/// size for the log buffer. individual logs may be larger than this value. +/// size for the buffer used to format individual values to string +/// while preparing an error message. This is only used for formatting +/// individual values in the message; final messages will be larger +/// than this value (see @ref RYML_ERRMSG_SIZE). This is also used for +/// the detailed debug log messages when RYML_DBG is defined. #define RYML_LOGBUF_SIZE (256) #endif #ifndef RYML_LOGBUF_SIZE_MAX -/// size for the fallback larger log buffer. +/// size for the fallback larger log buffer. When @ref +/// RYML_LOGBUG_SIZE is not large enough to convert a value to string, +/// then temporary stack memory is allocated up to +/// RYML_LOGBUF_SIZE_MAX. This limit is in place to prevent a stack +/// overflow. If the printed value requires more than +/// RYML_LOGBUF_SIZE_MAX, the value is silently skipped. #define RYML_LOGBUF_SIZE_MAX (1024) #endif -#ifndef RYML_ERRMSG_SIZE -/// size for the error message buffer -#define RYML_ERRMSG_SIZE (1024) +#ifndef RYML_LOCATIONS_SMALL_THRESHOLD +/// threshold at which a location search will revert from linear to +/// binary search. +#define RYML_LOCATIONS_SMALL_THRESHOLD (30) #endif @@ -547,13 +566,23 @@ struct _SubstrWriter namespace detail { // dumpfn is a function abstracting prints to terminal (or to string). template -C4_NO_INLINE void _parse_dump(DumpFn &&dumpfn, csubstr fmt, Args&& ...args) +C4_NO_INLINE void _dump(DumpFn &&dumpfn, csubstr fmt, Args&& ...args) { - // buffer for converting individual arguments. - char writebuf[RYML_LOGBUF_SIZE]; - auto results = format_dump_resume(std::forward(dumpfn), writebuf, fmt, std::forward(args)...); - // resume writing if the results failed to fit the buffer - if(C4_UNLIKELY(results.bufsize > RYML_LOGBUF_SIZE)) // bufsize will be that of the largest element serialized. Eg int(1) will require 1 byte. + DumpResults results; + // try writing everything: + { + // buffer for converting individual arguments. it is defined + // in a child scope to free it in case the buffer is too small + // for any of the arguments. + char writebuf[RYML_LOGBUF_SIZE]; + results = format_dump_resume(std::forward(dumpfn), writebuf, fmt, std::forward(args)...); + } + // if any of the arguments failed to fit the buffer, allocate a + // larger buffer (up to a limit) and resume writing. + // + // results.bufsize is set to the size of the largest element + // serialized. Eg int(1) will require 1 byte. + if(C4_UNLIKELY(results.bufsize > RYML_LOGBUF_SIZE)) { const size_t bufsize = results.bufsize <= RYML_LOGBUF_SIZE_MAX ? results.bufsize : RYML_LOGBUF_SIZE_MAX; #ifdef C4_MSVC @@ -570,7 +599,7 @@ C4_NORETURN C4_NO_INLINE void _report_err(Callbacks const& C4_RESTRICT callbacks char errmsg[RYML_ERRMSG_SIZE] = {0}; detail::_SubstrWriter writer(errmsg); auto dumpfn = [&writer](csubstr s){ writer.append(s); }; - _parse_dump(dumpfn, fmt, args...); + _dump(dumpfn, fmt, args...); writer.append('\n'); const size_t len = writer.pos < RYML_ERRMSG_SIZE ? writer.pos : RYML_ERRMSG_SIZE; callbacks.m_error(errmsg, len, {}, callbacks.m_user_data); diff --git a/src/c4/yml/parse_engine.def.hpp b/src/c4/yml/parse_engine.def.hpp index cc54abb9..b28e290e 100644 --- a/src/c4/yml/parse_engine.def.hpp +++ b/src/c4/yml/parse_engine.def.hpp @@ -379,13 +379,13 @@ void ParseEngine::_fmt_msg(DumpFn &&dumpfn) const size_t offs = 3u + to_chars(substr{}, st->pos.line) + to_chars(substr{}, st->pos.col); if(m_file.len) { - detail::_parse_dump(dumpfn, "{}:", m_file); + detail::_dump(dumpfn, "{}:", m_file); offs += m_file.len + 1; } - detail::_parse_dump(dumpfn, "{}:{}: ", st->pos.line, st->pos.col); + detail::_dump(dumpfn, "{}:{}: ", st->pos.line, st->pos.col); csubstr maybe_full_content = (contents.len < 80u ? contents : contents.first(80u)); csubstr maybe_ellipsis = (contents.len < 80u ? csubstr{} : csubstr("...")); - detail::_parse_dump(dumpfn, "{}{} (size={})\n", maybe_full_content, maybe_ellipsis, contents.len); + detail::_dump(dumpfn, "{}{} (size={})\n", maybe_full_content, maybe_ellipsis, contents.len); // highlight the remaining portion of the previous line size_t firstcol = (size_t)(lc.rem.begin() - lc.full.begin()); size_t lastcol = firstcol + lc.rem.len; @@ -394,7 +394,7 @@ void ParseEngine::_fmt_msg(DumpFn &&dumpfn) const dumpfn("^"); for(size_t i = 1, e = (lc.rem.len < 80u ? lc.rem.len : 80u); i < e; ++i) dumpfn("~"); - detail::_parse_dump(dumpfn, "{} (cols {}-{})\n", maybe_ellipsis, firstcol+1, lastcol+1); + detail::_dump(dumpfn, "{} (cols {}-{})\n", maybe_ellipsis, firstcol+1, lastcol+1); } else { @@ -405,7 +405,7 @@ void ParseEngine::_fmt_msg(DumpFn &&dumpfn) const // next line: print the state flags { char flagbuf_[128]; - detail::_parse_dump(dumpfn, "top state: {}\n", detail::_parser_flags_to_str(flagbuf_, m_state->flags)); + detail::_dump(dumpfn, "top state: {}\n", detail::_parser_flags_to_str(flagbuf_, m_state->flags)); } #endif } @@ -420,7 +420,7 @@ void ParseEngine::_err(csubstr fmt, Args const& C4_RESTRICT ...arg char errmsg[RYML_ERRMSG_SIZE]; detail::_SubstrWriter writer(errmsg); auto dumpfn = [&writer](csubstr s){ writer.append(s); }; - detail::_parse_dump(dumpfn, fmt, args...); + detail::_dump(dumpfn, fmt, args...); writer.append('\n'); _fmt_msg(dumpfn); size_t len = writer.pos < RYML_ERRMSG_SIZE ? writer.pos : RYML_ERRMSG_SIZE; @@ -438,7 +438,7 @@ void ParseEngine::_dbg(csubstr fmt, Args const& C4_RESTRICT ...arg if(_dbg_enabled()) { auto dumpfn = [](csubstr s){ if(s.str) fwrite(s.str, 1, s.len, stdout); }; - detail::_parse_dump(dumpfn, fmt, args...); + detail::_dump(dumpfn, fmt, args...); dumpfn("\n"); _fmt_msg(dumpfn); } @@ -3837,7 +3837,6 @@ Location ParseEngine::val_location(const char *val) const { if(C4_UNLIKELY(val == nullptr)) return {m_file, 0, 0, 0}; - _RYML_CB_CHECK(m_evt_handler->m_stack.m_callbacks, m_options.locations()); // NOTE: if any of these checks fails, the parser needs to be // instantiated with locations enabled. @@ -3855,7 +3854,7 @@ Location ParseEngine::val_location(const char *val) const using lineptr_type = size_t const* C4_RESTRICT; lineptr_type lineptr = nullptr; size_t offset = (size_t)(val - src.begin()); - if(m_newline_offsets_size < 30) // TODO magic number + if(m_newline_offsets_size < RYML_LOCATIONS_SMALL_THRESHOLD) { // just do a linear search if the size is small. for(lineptr_type curr = m_newline_offsets, last = m_newline_offsets + m_newline_offsets_size; curr < last; ++curr) @@ -3872,7 +3871,7 @@ Location ParseEngine::val_location(const char *val) const // do a bisection search if the size is not small. // // We could use std::lower_bound but this is simple enough and - // spares the include of . + // spares the costly include of . size_t count = m_newline_offsets_size; size_t step; lineptr_type it; diff --git a/src/c4/yml/parse_engine.hpp b/src/c4/yml/parse_engine.hpp index 17729950..8231b393 100644 --- a/src/c4/yml/parse_engine.hpp +++ b/src/c4/yml/parse_engine.hpp @@ -15,6 +15,7 @@ # pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/) #endif + namespace c4 { namespace yml { diff --git a/test/test_callbacks.cpp b/test/test_callbacks.cpp index fc2da590..caeea1e8 100644 --- a/test/test_callbacks.cpp +++ b/test/test_callbacks.cpp @@ -418,7 +418,7 @@ TEST(_parse_dump, small_args) } { Dumper dumper; - detail::_parse_dump(dumper, fmt, str); + detail::_dump(dumper, fmt, str); EXPECT_EQ(dumper.writer.curr(), to_csubstr(expected)); } } @@ -437,7 +437,7 @@ TEST(_parse_dump, large_args) } { Dumper dumper; - detail::_parse_dump(dumper, fmt, str); + detail::_dump(dumper, fmt, str); const std::string expected = formatrs(fmt, str); EXPECT_EQ(dumper.writer.curr(), to_csubstr(expected)); } @@ -457,7 +457,7 @@ TEST(_parse_dump, unprintable_args) } { Dumper dumper; - detail::_parse_dump(dumper, fmt, str); + detail::_dump(dumper, fmt, str); size_t unprintable_size = (size_t)(RYML_LOGBUF_SIZE_MAX+1); const std::string zeros(/*count*/unprintable_size, '\0'); EXPECT_EQ(to_csubstr(zeros).len, unprintable_size);