From 82e7b8300ac2d565428607f5896a3984693ba7ec Mon Sep 17 00:00:00 2001 From: Pauli Date: Sun, 10 Jun 2018 14:57:42 +0300 Subject: [PATCH] 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. --- library/MiscUtils.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 91a860991..ee64ffaee 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -40,6 +40,7 @@ distribution. #include #include +#include std::string stl_sprintf(const char *fmt, ...) { 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::vector buf; - buf.resize(4096); - for (;;) { - va_list args2; - va_copy(args2, args); - int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2); - va_end(args2); - - if (rsz < 0) - buf.resize(buf.size()*2); - else if (unsigned(rsz) >= buf.size()) - buf.resize(rsz+1); - else - return std::string(&buf[0], rsz); - } + /* Allow small (about single line) strings to be printed into stack memory + * with a call to vsnprintf. + */ + std::array buf; + va_list args2; + va_copy(args2, args); + int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2); + va_end(args2); + if (rsz < 0) + return std::string(); /* Error occurred */ + if (static_cast(rsz) < buf.size()) + return std::string(&buf[0], rsz); /* Whole string fits to a single line buffer */ + std::string rv; + // 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 *out,