You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2018/03/20 14:46:26 UTC

[trafficserver] branch master updated: BufferRewrite: Add support for MemSpan, string_view hex dump, and std::string.

This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 18b2ba4  BufferRewrite: Add support for MemSpan, string_view hex dump, and std::string.
18b2ba4 is described below

commit 18b2ba44f04810b5da6af64692e660d857b9d645
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Fri Mar 16 08:38:07 2018 -0500

    BufferRewrite: Add support for MemSpan, string_view hex dump, and std::string.
---
 lib/ts/BufferWriter.h                        |  61 +++++++--
 lib/ts/BufferWriterFormat.cc                 | 177 +++++++++++++++++++--------
 lib/ts/BufferWriterForward.h                 |  32 ++---
 lib/ts/CryptoHash.h                          |  15 +++
 lib/ts/MemSpan.h                             |  14 ++-
 lib/ts/unit-tests/test_BufferWriterFormat.cc | 112 ++++++++++++++---
 6 files changed, 321 insertions(+), 90 deletions(-)

diff --git a/lib/ts/BufferWriter.h b/lib/ts/BufferWriter.h
index 71944fa..370a697 100644
--- a/lib/ts/BufferWriter.h
+++ b/lib/ts/BufferWriter.h
@@ -28,10 +28,11 @@
 #include <utility>
 #include <cstring>
 #include <vector>
-#include <map>
+#include <string>
 #include <ts/ink_std_compat.h>
 
 #include <ts/TextView.h>
+#include <ts/MemSpan.h>
 #include <ts/ink_assert.h>
 #include <ts/BufferWriterForward.h>
 
@@ -558,8 +559,9 @@ BufferWriter::print(TextView fmt, Rest... rest)
     if (spec_p) {
       BWFSpec spec{spec_v};
       size_t width = this->remaining();
-      if (spec._max > 0)
-        width = std::min(width, static_cast<size_t>(spec._max));
+      if (spec._max < width) {
+        width = spec._max;
+      }
       FixedBufferWriter lw{this->auxBuffer(), width};
 
       if (spec._name.size() == 0) {
@@ -580,7 +582,7 @@ BufferWriter::print(TextView fmt, Rest... rest)
           lw.write(msg).write(spec._name).write('}');
         }
       }
-      if (lw.size()) {
+      if (lw.extent()) {
         bw_fmt::Do_Alignment(spec, *this, lw);
       }
       ++arg_idx;
@@ -599,9 +601,8 @@ BufferWriter::print(BWFormat const &fmt, Rest... rest)
 
   for (BWFormat::Item const &item : fmt._items) {
     size_t width = this->remaining();
-    size_t max   = item._spec._max;
-    if (max && max < width) {
-      width = max;
+    if (item._spec._max < width) {
+      width = item._spec._max;
     }
     FixedBufferWriter lw{this->auxBuffer(), width};
     if (item._gf) {
@@ -627,6 +628,20 @@ operator<<(BufferWriter &w, V &&v)
   return bwformat(w, BWFSpec::DEFAULT, std::forward<V>(v));
 }
 
+// Pointers
+inline BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, const void *ptr)
+{
+  BWFSpec ptr_spec{spec};
+  ptr_spec._radix_lead_p = true;
+  if (ptr_spec._type == BWFSpec::DEFAULT_TYPE)
+    ptr_spec._type = 'x'; // if default, switch to hex.
+  return bw_fmt::Format_Integer(w, ptr_spec, reinterpret_cast<intptr_t>(ptr), false);
+}
+
+// MemSpan
+BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, MemSpan const &span);
+
 // -- Common formatters --
 
 BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, string_view sv);
@@ -637,10 +652,22 @@ bwformat(BufferWriter &w, BWFSpec const &, char c)
   return w.write(c);
 }
 
+template <size_t N>
+BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, const char (&a)[N])
+{
+  return bwformat(w, spec, string_view(a, N - 1));
+}
+
 inline BufferWriter &
 bwformat(BufferWriter &w, BWFSpec const &spec, const char *v)
 {
-  return bwformat(w, spec, string_view(v));
+  if (spec._type == 'x' || spec._type == 'X') {
+    bwformat(w, spec, static_cast<const void *>(v));
+  } else {
+    bwformat(w, spec, string_view(v));
+  }
+  return w;
 }
 
 inline BufferWriter &
@@ -681,6 +708,24 @@ operator<<(BufferWriter &w, int const &i)
   return bwformat(w, BWFSpec::DEFAULT, static_cast<intmax_t>(i));
 }
 
+// std::string support
+/** Print to a @c std::string
+
+    Print to the string @a s. If there is overflow then resize the string sufficiently to hold the output
+    and print again. The effect is the string is resized only as needed to hold the output.
+ */
+template <typename... Rest>
+void
+bwprint(std::string &s, ts::TextView fmt, Rest &&... rest)
+{
+  auto len = s.size();
+  size_t n = ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).print(fmt, std::forward<Rest>(rest)...).extent();
+  s.resize(n);   // always need to resize - if shorter, must clip pre-existing text.
+  if (n > len) { // dropped data, try again.
+    ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).print(fmt, std::forward<Rest>(rest)...);
+  }
+}
+
 } // end namespace ts
 
 #endif // include once
diff --git a/lib/ts/BufferWriterFormat.cc b/lib/ts/BufferWriterFormat.cc
index f49482a..2145a7d 100644
--- a/lib/ts/BufferWriterFormat.cc
+++ b/lib/ts/BufferWriterFormat.cc
@@ -184,41 +184,73 @@ namespace bw_fmt
   void
   Do_Alignment(BWFSpec const &spec, BufferWriter &w, BufferWriter &lw)
   {
-    size_t size = lw.size();
-    size_t min  = spec._min;
-    if (size < min) {
-      size_t delta = min - size; // note - size <= extent -> size < min
+    size_t extent = lw.extent();
+    size_t min    = spec._min;
+    size_t size   = lw.size();
+    if (extent < min) {
+      size_t delta = min - extent;
+      char *base   = w.auxBuffer();        // should be first byte of @a lw e.g. lw.data() - avoid const_cast.
+      char *limit  = base + lw.capacity(); // first invalid byte.
+      char *dst;                           // used to track memory operation targest;
+      char *last;                          // track limit of memory operation.
+      size_t d2;
       switch (spec._align) {
-      case BWFSpec::Align::NONE: // same as LEFT for output.
-      case BWFSpec::Align::LEFT:
-        w.fill(size);
-        while (delta--)
-          w.write(spec._fill);
-        break;
       case BWFSpec::Align::RIGHT:
-        std::memmove(w.auxBuffer() + delta, w.auxBuffer(), size);
-        while (delta--)
-          w.write(spec._fill);
-        w.fill(size);
+        dst = base + delta; // move existing content to here.
+        if (dst < limit) {
+          last = dst + size; // amount of data to move.
+          if (last > limit)
+            last = limit;
+          std::memmove(dst, base, last - dst);
+        }
+        dst  = base;
+        last = base + delta;
+        if (last > limit)
+          last = limit;
+        while (dst < last)
+          *dst++ = spec._fill;
         break;
       case BWFSpec::Align::CENTER:
-        if (delta > 1) {
-          size_t d2 = delta / 2;
-          std::memmove(w.auxBuffer() + (delta / 2), w.auxBuffer(), size);
-          while (d2--)
-            w.write(spec._fill);
+        d2 = (delta + 1) / 2;
+        if (d2 > 1) {
+          dst = base + d2; // move existing content to here.
+          if (dst < limit) {
+            last = dst + size; // amount of data to move.
+            if (last > limit)
+              last = limit;
+            std::memmove(dst, base, last - dst);
+          }
+          dst  = base + size + d2;
+          last = base + delta / 2;
+          if (last > limit)
+            last = limit;
+          while (dst < last)
+            *dst++ = spec._fill;
         }
-        w.fill(size);
-        delta = (delta + 1) / 2;
-        while (delta--)
-          w.write(spec._fill);
+        dst  = base;
+        last = base + d2;
+        if (last > limit)
+          last = limit;
+        while (dst < last)
+          *dst++ = spec._fill;
         break;
-      case BWFSpec::Align::SIGN:
-        w.fill(size);
+      default:
+        // Everything else is equivalent to LEFT - distinction is for more specialized
+        // types such as integers.
+        dst  = base + size;
+        last = dst + delta;
+        if (last > limit)
+          last = limit;
+        while (dst < last)
+          *dst++ = spec._fill;
         break;
       }
+      w.fill(min);
     } else {
-      w.fill(size);
+      size_t max = spec._max;
+      if (max < extent)
+        extent = max;
+      w.fill(extent);
     }
   }
 
@@ -371,6 +403,47 @@ namespace bw_fmt
     return w;
   }
 
+  /// Write out the @a data as hexadecimal, using @a digits as the conversion.
+  void
+  Hex_Dump(BufferWriter &w, string_view data, const char *digits)
+  {
+    const char *ptr = data.data();
+    for (auto n = data.size(); n > 0; --n) {
+      char c = *ptr++;
+      w.write(digits[(c >> 4) & 0xF]);
+      w.write(digits[c & 0xf]);
+    }
+  }
+
+  template <typename F>
+  void
+  Write_Aligned(BufferWriter &w, F const &f, BWFSpec::Align align, int width, char fill)
+  {
+    switch (align) {
+    case BWFSpec::Align::LEFT:
+    case BWFSpec::Align::SIGN:
+      f();
+      while (width-- > 0)
+        w.write(fill);
+      break;
+    case BWFSpec::Align::RIGHT:
+      while (width-- > 0)
+        w.write(fill);
+      f();
+      break;
+    case BWFSpec::Align::CENTER:
+      for (int i = width / 2; i > 0; --i)
+        w.write(fill);
+      f();
+      for (int i = (width + 1) / 2; i > 0; --i)
+        w.write(fill);
+      break;
+    default:
+      f();
+      break;
+    }
+  }
+
 } // bw_fmt
 
 BufferWriter &
@@ -380,29 +453,35 @@ bwformat(BufferWriter &w, BWFSpec const &spec, string_view sv)
   if (spec._prec > 0)
     sv.remove_prefix(spec._prec);
 
-  width -= sv.size();
-  switch (spec._align) {
-  case BWFSpec::Align::LEFT:
-  case BWFSpec::Align::SIGN:
-    w.write(sv);
-    while (width-- > 0)
-      w.write(spec._fill);
-    break;
-  case BWFSpec::Align::RIGHT:
-    while (width-- > 0)
-      w.write(spec._fill);
-    w.write(sv);
-    break;
-  case BWFSpec::Align::CENTER:
-    for (int i = width / 2; i > 0; --i)
-      w.write(spec._fill);
-    w.write(sv);
-    for (int i = (width + 1) / 2; i > 0; --i)
-      w.write(spec._fill);
-    break;
-  default:
-    w.write(sv);
-    break;
+  if ('x' == spec._type || 'X' == spec._type) {
+    const char *digits = 'x' == spec._type ? bw_fmt::LOWER_DIGITS : bw_fmt::UPPER_DIGITS;
+    width -= sv.size() * 2;
+    if (spec._radix_lead_p) {
+      w.write('0');
+      w.write(spec._type);
+      width -= 2;
+    }
+    bw_fmt::Write_Aligned(w, [&w, &sv, digits]() { bw_fmt::Hex_Dump(w, sv, digits); }, spec._align, width, spec._fill);
+  } else {
+    width -= sv.size();
+    bw_fmt::Write_Aligned(w, [&w, &sv]() { w.write(sv); }, spec._align, width, spec._fill);
+  }
+  return w;
+}
+
+BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, MemSpan const &span)
+{
+  static const BWFormat default_fmt{"{:#x}@{}"};
+  if (spec._ext.size() && 'd' == spec._ext.front()) {
+    const char *digits = 'X' == spec._type ? bw_fmt::UPPER_DIGITS : bw_fmt::LOWER_DIGITS;
+    if (spec._radix_lead_p) {
+      w.write('0');
+      w.write(digits[33]);
+    }
+    bw_fmt::Hex_Dump(w, string_view{static_cast<char *>(span.data()), span.usize()}, digits);
+  } else {
+    w.print(default_fmt, span.size(), span.data());
   }
   return w;
 }
diff --git a/lib/ts/BufferWriterForward.h b/lib/ts/BufferWriterForward.h
index aca64e8..388e00b 100644
--- a/lib/ts/BufferWriterForward.h
+++ b/lib/ts/BufferWriterForward.h
@@ -38,7 +38,9 @@ namespace ts
 /** A parsed version of a format specifier.
  */
 struct BWFSpec {
-  using self_type = BWFSpec; ///< Self reference type.
+  using self_type                    = BWFSpec; ///< Self reference type.
+  static constexpr char DEFAULT_TYPE = 'g';     ///< Default format type.
+
   /// Constructor a default instance.
   constexpr BWFSpec() {}
 
@@ -48,21 +50,21 @@ struct BWFSpec {
   char _fill = ' '; ///< Fill character.
   char _sign = '-'; ///< Numeric sign style, space + -
   enum class Align : char {
-    NONE,                           ///< No alignment.
-    LEFT,                           ///< Left alignment '<'.
-    RIGHT,                          ///< Right alignment '>'.
-    CENTER,                         ///< Center alignment '='.
-    SIGN                            ///< Align plus/minus sign before numeric fill. '^'
-  } _align           = Align::NONE; ///< Output field alignment.
-  char _type         = 'g';         ///< Type / radix indicator.
-  bool _radix_lead_p = false;       ///< Print leading radix indication.
+    NONE,                            ///< No alignment.
+    LEFT,                            ///< Left alignment '<'.
+    RIGHT,                           ///< Right alignment '>'.
+    CENTER,                          ///< Center alignment '='.
+    SIGN                             ///< Align plus/minus sign before numeric fill. '^'
+  } _align           = Align::NONE;  ///< Output field alignment.
+  char _type         = DEFAULT_TYPE; ///< Type / radix indicator.
+  bool _radix_lead_p = false;        ///< Print leading radix indication.
   // @a _min is unsigned because there's no point in an invalid default, 0 works fine.
-  unsigned int _min = 0;  ///< Minimum width.
-  int _prec         = -1; ///< Precision
-  unsigned int _max = 0;  ///< Maxium width
-  int _idx          = -1; ///< Positional "name" of the specification.
-  string_view _name;      ///< Name of the specification.
-  string_view _ext;       ///< Extension if provided.
+  unsigned int _min = 0;                                        ///< Minimum width.
+  int _prec         = -1;                                       ///< Precision
+  unsigned int _max = std::numeric_limits<unsigned int>::max(); ///< Maxium width
+  int _idx          = -1;                                       ///< Positional "name" of the specification.
+  string_view _name;                                            ///< Name of the specification.
+  string_view _ext;                                             ///< Extension if provided.
 
   static const self_type DEFAULT;
 
diff --git a/lib/ts/CryptoHash.h b/lib/ts/CryptoHash.h
index 973d6b3..650a90f 100644
--- a/lib/ts/CryptoHash.h
+++ b/lib/ts/CryptoHash.h
@@ -23,6 +23,9 @@
 #if !defined CRYPTO_HASH_HEADER
 #define CRYPTO_HASH_HEADER
 
+#include <ts/BufferWriter.h>
+#include <ts/string_view.h>
+
 /// Apache Traffic Server commons.
 
 #if TS_ENABLE_FIPS == 1
@@ -178,6 +181,18 @@ CryptoContext::finalize(CryptoHash &hash)
 
 } // end namespace
 
+namespace ts
+{
+inline BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, ats::CryptoHash const &hash)
+{
+  BWFSpec local_spec{spec};
+  if ('X' != local_spec._type)
+    local_spec._type = 'x';
+  return bwformat(w, local_spec, ts::string_view(reinterpret_cast<const char *>(hash.u8), CRYPTO_HASH_SIZE));
+}
+} // ts
+
 using ats::CryptoHash;
 using ats::CryptoContext;
 using ats::CRYPTO_HASH_ZERO;
diff --git a/lib/ts/MemSpan.h b/lib/ts/MemSpan.h
index 8f4f873..0a0c6f5 100644
--- a/lib/ts/MemSpan.h
+++ b/lib/ts/MemSpan.h
@@ -141,6 +141,8 @@ public:
 
   /// Number of bytes in the span.
   constexpr ptrdiff_t size() const;
+  /// Number of bytes in the span (unsigned).
+  constexpr size_t usize() const;
 
   /// Memory pointer.
   /// @note This is equivalent to @c begin currently but it's probably good to have separation.
@@ -426,6 +428,12 @@ MemSpan::size() const
   return _size;
 }
 
+inline constexpr size_t
+MemSpan::usize() const
+{
+  return _size;
+}
+
 inline MemSpan &
 MemSpan::operator=(MemSpan const &that)
 {
@@ -559,13 +567,11 @@ MemSpan::find_if(F const &pred)
 
 namespace std
 {
-ostream &
+inline ostream &
 operator<<(ostream &os, const ts::MemSpan &b)
 {
   if (os.good()) {
-    ostringstream out;
-    out << b.size() << '@' << hex << b.data();
-    os << out.str();
+    os << b.size() << '@' << hex << b.data();
   }
   return os;
 }
diff --git a/lib/ts/unit-tests/test_BufferWriterFormat.cc b/lib/ts/unit-tests/test_BufferWriterFormat.cc
index 2ea8cba..6431803 100644
--- a/lib/ts/unit-tests/test_BufferWriterFormat.cc
+++ b/lib/ts/unit-tests/test_BufferWriterFormat.cc
@@ -22,9 +22,12 @@
  */
 
 #include "catch.hpp"
-#include <ts/BufferWriter.h>
 #include <chrono>
 #include <iostream>
+#include <ts/BufferWriter.h>
+#include <ts/MemSpan.h>
+#include <ts/INK_MD5.h>
+#include <ts/CryptoHash.h>
 
 TEST_CASE("Buffer Writer << operator", "[bufferwriter][stream]")
 {
@@ -143,6 +146,90 @@ TEST_CASE("BWFormat", "[bwprint][bwformat]")
   bw.reduce(0);
   bw.print("Text: _{0:-<20.52,20}_", text);
   REQUIRE(bw.view() == "Text: _QRSTUVWXYZ----------_");
+
+  void *ptr = reinterpret_cast<void *>(0XBADD0956);
+  bw.reduce(0);
+  bw.print("{}", ptr);
+  REQUIRE(bw.view() == "0xbadd0956");
+  bw.reduce(0);
+  bw.print("{:X}", ptr);
+  REQUIRE(bw.view() == "0XBADD0956");
+  int *int_ptr = static_cast<int *>(ptr);
+  bw.reduce(0);
+  bw.print("{}", int_ptr);
+  REQUIRE(bw.view() == "0xbadd0956");
+  auto char_ptr = "good";
+  bw.reduce(0);
+  bw.print("{:x}", static_cast<char *>(ptr));
+  REQUIRE(bw.view() == "0xbadd0956");
+  bw.reduce(0);
+  bw.print("{}", char_ptr);
+  REQUIRE(bw.view() == "good");
+
+  ts::MemSpan span{ptr, 0x200};
+  bw.reduce(0);
+  bw.print("{}", span);
+  REQUIRE(bw.view() == "0x200@0xbadd0956");
+
+  bw.reduce(0);
+  bw.print("{::d}", ts::MemSpan(const_cast<char *>(char_ptr), 4));
+  REQUIRE(bw.view() == "676f6f64");
+  bw.reduce(0);
+  bw.print("{:#:d}", ts::MemSpan(const_cast<char *>(char_ptr), 4));
+  REQUIRE(bw.view() == "0x676f6f64");
+
+  ts::string_view sv{"abc123"};
+  bw.reduce(0);
+  bw.print("{}", sv);
+  REQUIRE(bw.view() == sv);
+  bw.reduce(0);
+  bw.print("{:x}", sv);
+  REQUIRE(bw.view() == "616263313233");
+  bw.reduce(0);
+  bw.print("{:#x}", sv);
+  REQUIRE(bw.view() == "0x616263313233");
+  bw.reduce(0);
+  bw.print("|{:16x}|", sv);
+  REQUIRE(bw.view() == "|616263313233    |");
+  bw.reduce(0);
+  bw.print("|{:>16x}|", sv);
+  REQUIRE(bw.view() == "|    616263313233|");
+  bw.reduce(0);
+  bw.print("|{:=16x}|", sv);
+  REQUIRE(bw.view() == "|  616263313233  |");
+  bw.reduce(0);
+  bw.print("|{:>16.2x}|", sv);
+  REQUIRE(bw.view() == "|        63313233|");
+  bw.reduce(0);
+  bw.print("|{:<0.2,5x}|", sv);
+  REQUIRE(bw.view() == "|63313|");
+
+  INK_MD5 md5;
+  bw.reduce(0);
+  bw.print("{}", md5);
+  REQUIRE(bw.view() == "00000000000000000000000000000000");
+  CryptoContext().hash_immediate(md5, sv.data(), sv.size());
+  bw.reduce(0);
+  bw.print("{}", md5);
+  REQUIRE(bw.view() == "e99a18c428cb38d5f260853678922e03");
+}
+
+TEST_CASE("bwstring", "[bwprint][bwstring]")
+{
+  std::string s;
+  size_t n;
+  ts::TextView fmt("{} -- {}");
+  ts::string_view text{"e99a18c428cb38d5f260853678922e03"};
+
+  bwprint(s, fmt, "string", 956);
+  REQUIRE(s.size() == 13);
+  REQUIRE(s == "string -- 956");
+
+  bwprint(s, fmt, 99999, text);
+  REQUIRE(s == "99999 -- e99a18c428cb38d5f260853678922e03");
+
+  bwprint(s, "{} .. |{:,20}|", 32767, text);
+  REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|");
 }
 
 // Normally there's no point in running the performance tests, but it's worth keeping the code
@@ -155,34 +242,31 @@ TEST_CASE("bwperf", "[bwprint][performance]")
   auto delta = std::chrono::high_resolution_clock::now() - start;
   constexpr int N_LOOPS = 1000000;
 
-  ts::string_view text{"Format |"};
+  static constexpr const char * FMT = "Format |{:#010x}| '{}'";
+  static constexpr ts::TextView fmt{FMT, strlen(FMT)};
+  static constexpr ts::string_view text{"e99a18c428cb38d5f260853678922e03"_sv};
   ts::LocalBufferWriter<256> bw;
 
   ts::BWFSpec spec;
 
-  start = std::chrono::high_resolution_clock::now();
-  for (int i = 0; i < N_LOOPS; ++i) {
-    bw.reduce(0);
-    bw.print( "Format |{:#010x}|", -956);
-  }
-  delta = std::chrono::high_resolution_clock::now() - start;
-  std::cout << "BW Timing is " << delta.count() << "ns or " << std::chrono::duration_cast<std::chrono::milliseconds>(delta).count()
-            << "ms" << std::endl;
+  bw.reduce(0);
+  bw.print(fmt, -956, text);
+  REQUIRE(bw.view() == "Format |-0x00003bc| 'e99a18c428cb38d5f260853678922e03'");
 
   start = std::chrono::high_resolution_clock::now();
   for (int i = 0; i < N_LOOPS; ++i) {
     bw.reduce(0);
-    bw.print("Format |{:#010x}|", -956);
+    bw.print(fmt, -956, text);
   }
   delta = std::chrono::high_resolution_clock::now() - start;
   std::cout << "bw.print() " << delta.count() << "ns or " << std::chrono::duration_cast<std::chrono::milliseconds>(delta).count()
             << "ms" << std::endl;
 
-  ts::BWFormat fmt("Format |{:#010x}|");
+  ts::BWFormat pre_fmt(fmt);
   start = std::chrono::high_resolution_clock::now();
   for (int i = 0; i < N_LOOPS; ++i) {
     bw.reduce(0);
-    bw.print( fmt, -956);
+    bw.print(pre_fmt, -956, text);
   }
   delta = std::chrono::high_resolution_clock::now() - start;
   std::cout << "Preformatted: " << delta.count() << "ns or "
@@ -191,7 +275,7 @@ TEST_CASE("bwperf", "[bwprint][performance]")
   char buff[256];
   start = std::chrono::high_resolution_clock::now();
   for (int i = 0; i < N_LOOPS; ++i) {
-    snprintf(buff, sizeof(buff), "Format |%#0x10|", -956);
+    snprintf(buff, sizeof(buff), "Format |%#0x10| '%.*s'", -956, static_cast<int>(text.size()), text.data());
   }
   delta = std::chrono::high_resolution_clock::now() - start;
   std::cout << "snprint Timing is " << delta.count() << "ns or "

-- 
To stop receiving notification emails like this one, please contact
amc@apache.org.