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.