Make stl_vsprintf return if there is an vsnprintf error

vsnprintf man page claims:
"If an output error is encountered, a negative value is returned."

That means we has to call vsnprintf twice at most to have whole output
written to a string. But in case of error we return an empty string.

The code also optimizes an expected common case of outputting single
line with a small stack allocated buffer. If the stack buffer is too
small then it uses std::string::resize to allocate exactly enough memory
and writes directly to std::string.

Second call could use vsprintf because memory is known to be large
enough. But I think that difference isn't detectable outside micro
benchmarks.
develop
Pauli 2018-06-10 14:57:42 +03:00
parent 69cf5756c3
commit 82e7b8300a
1 changed files with 18 additions and 15 deletions

@ -40,6 +40,7 @@ distribution.
#include <sstream> #include <sstream>
#include <map> #include <map>
#include <array>
std::string stl_sprintf(const char *fmt, ...) { std::string stl_sprintf(const char *fmt, ...) {
va_list lst; va_list lst;
@ -50,21 +51,23 @@ std::string stl_sprintf(const char *fmt, ...) {
} }
std::string stl_vsprintf(const char *fmt, va_list args) { std::string stl_vsprintf(const char *fmt, va_list args) {
std::vector<char> buf; /* Allow small (about single line) strings to be printed into stack memory
buf.resize(4096); * with a call to vsnprintf.
for (;;) { */
va_list args2; std::array<char,128> buf;
va_copy(args2, args); va_list args2;
int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2); va_copy(args2, args);
va_end(args2); int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2);
va_end(args2);
if (rsz < 0) if (rsz < 0)
buf.resize(buf.size()*2); return std::string(); /* Error occurred */
else if (unsigned(rsz) >= buf.size()) if (static_cast<unsigned>(rsz) < buf.size())
buf.resize(rsz+1); return std::string(&buf[0], rsz); /* Whole string fits to a single line buffer */
else std::string rv;
return std::string(&buf[0], rsz); // Allocate enough memory for the output and null termination
} rv.resize(rsz+1);
rsz = vsnprintf(&rv[0], rv.size(), fmt, args);
return rv;
} }
bool split_string(std::vector<std::string> *out, bool split_string(std::vector<std::string> *out,