aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorYuri Wiitala <miu@chromium.org>2020-10-26 12:30:13 -0700
committerCommit Bot <commit-bot@chromium.org>2020-10-26 20:44:44 +0000
commit2837c9f0c416803cbab994f1fe10296d2a132971 (patch)
treedae3d9a0167f1bc6a56ee65be4f329ce086b73ed /util
parent83e50af3e0bd628e7307e67e0ae1c6a611b8d7bb (diff)
downloadopenscreen-2837c9f0c416803cbab994f1fe10296d2a132971.tar.gz
Add StringPrintf() wrapper utility.
Adds an efficient wrapper around std::snprintf() that returns the formatted result in a std::string. Background: The issue of either 1) wanting to use absl::StrFormat() without bloating our binary sizes, or 2) manually writing char[] buffer code around std::snprintf() has come up several times over the past year or so. So, this new utility is about reducing friction and improving future developer velocity. Change-Id: Ie898f91575852b06d59844dd564a0da03df1f7ed Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2487768 Commit-Queue: Yuri Wiitala <miu@chromium.org> Reviewed-by: Yuri Wiitala <miu@chromium.org> Reviewed-by: Brandon Tolsch <btolsch@chromium.org>
Diffstat (limited to 'util')
-rw-r--r--util/stringprintf.cc23
-rw-r--r--util/stringprintf.h13
-rw-r--r--util/stringprintf_unittest.cc9
3 files changed, 45 insertions, 0 deletions
diff --git a/util/stringprintf.cc b/util/stringprintf.cc
index 452c3282..2d9bba22 100644
--- a/util/stringprintf.cc
+++ b/util/stringprintf.cc
@@ -4,11 +4,34 @@
#include "util/stringprintf.h"
+#include <cstdarg>
+#include <cstdio>
#include <iomanip>
#include <sstream>
+#include "util/osp_logging.h"
+
namespace openscreen {
+std::string StringPrintf(const char* format, ...) {
+ va_list vlist;
+ va_start(vlist, format);
+ const int length = std::vsnprintf(nullptr, 0, format, vlist);
+ OSP_CHECK_GE(length, 0) << "Invalid format string: " << format;
+ va_end(vlist);
+
+ std::string result(length, '\0');
+ // Note: There's no need to add one for the extra terminating NUL char since
+ // the standard, since C++11, requires that "data() + size() points to [the
+ // NUL terminator]". Thus, std::vsnprintf() will write the NUL to a valid
+ // memory location.
+ va_start(vlist, format);
+ std::vsnprintf(&result[0], length + 1, format, vlist);
+ va_end(vlist);
+
+ return result;
+}
+
std::string HexEncode(absl::Span<const uint8_t> bytes) {
std::ostringstream hex_dump;
hex_dump << std::setfill('0') << std::hex;
diff --git a/util/stringprintf.h b/util/stringprintf.h
index df24ac04..0e7984e1 100644
--- a/util/stringprintf.h
+++ b/util/stringprintf.h
@@ -14,6 +14,19 @@
namespace openscreen {
+// Enable compile-time checking of the printf format argument, if available.
+#if defined(__GNUC__) || defined(__clang__)
+#define OSP_CHECK_PRINTF_ARGS(format_param, dots_param) \
+ __attribute__((format(printf, format_param, dots_param)))
+#else
+#define OSP_CHECK_PRINTF_ARGS(format_param, dots_param)
+#endif
+
+// Returns a std::string containing the results of a std::sprintf() call. This
+// is an efficient, zero-copy wrapper.
+[[nodiscard]] std::string StringPrintf(const char* format, ...)
+ OSP_CHECK_PRINTF_ARGS(1, 2);
+
template <typename It>
void PrettyPrintAsciiHex(std::ostream& os, It first, It last) {
auto it = first;
diff --git a/util/stringprintf_unittest.cc b/util/stringprintf_unittest.cc
index df0d60f8..e37e7cb6 100644
--- a/util/stringprintf_unittest.cc
+++ b/util/stringprintf_unittest.cc
@@ -9,6 +9,15 @@
namespace openscreen {
namespace {
+TEST(StringPrintf, ProducesFormattedStrings) {
+ EXPECT_EQ("no args", StringPrintf("no args"));
+ EXPECT_EQ("", StringPrintf("%s", ""));
+ EXPECT_EQ("42", StringPrintf("%d", 42));
+ EXPECT_EQ(
+ "The result of foo(1, 2) looks good!",
+ StringPrintf("The result of foo(%d, %d) looks %s%c", 1, 2, "good", '!'));
+}
+
TEST(HexEncode, ProducesEmptyStringFromEmptyByteArray) {
const uint8_t kSomeMemoryLocation = 0;
EXPECT_EQ("", HexEncode(absl::Span<const uint8_t>(&kSomeMemoryLocation, 0)));