diff --git a/src/ripple_basics/utility/StringUtilities.cpp b/src/ripple_basics/utility/StringUtilities.cpp index 228b338285d..c63e6249ec8 100644 --- a/src/ripple_basics/utility/StringUtilities.cpp +++ b/src/ripple_basics/utility/StringUtilities.cpp @@ -412,14 +412,41 @@ class StringUtilities_test : public beast::unit_test::suite "parseUrl: Mixed://domain/path path failed"); } + void testStringConcat () + { + testcase ("stringConcat"); + auto result = stringConcat({}); + expect(result == "", result); + + result = stringConcat({"hello, ", std::string("world.")}); + expect(result == "hello, world.", result); + + result = stringConcat({"hello, ", 23}); + expect(result == "hello, 23", result); + + result = stringConcat({"hello, ", true}); + expect(result == "hello, true", result); + + result = stringConcat({"hello, ", 'x'}); + expect(result == "hello, x", result); + } + + void testToString () + { + testcase ("toString"); + auto result = toString("hello"); + expect(result == "hello", result); + } + void run () { testParseUrl (); - testUnHex (); + testStringConcat (); + testToString (); } }; -BEAST_DEFINE_TESTSUITE(StringUtilities,ripple_basics,ripple); +BEAST_DEFINE_TESTSUITE(StringUtilities, ripple_basics, ripple); } // ripple diff --git a/src/ripple_basics/utility/StringUtilities.h b/src/ripple_basics/utility/StringUtilities.h index c7264e28406..9b760a66fee 100644 --- a/src/ripple_basics/utility/StringUtilities.h +++ b/src/ripple_basics/utility/StringUtilities.h @@ -26,7 +26,7 @@ namespace ripple { // Ripple specific constant used for parsing qualities and other things // -// NIKB TODO Why is this here instead of somewhere more sensible? What +// NIKB TODO Why is this here instead of somewhere more sensible? What // "other things" is this being used for? #define QUALITY_ONE 1000000000 // 10e9 @@ -131,6 +131,86 @@ bool parseUrl (const std::string& strUrl, std::string& strScheme, std::string& s extern beast::StringPairArray parseDelimitedKeyValueString (beast::String s, beast::beast_wchar delimiter='|'); +/** toString() generalizes std::to_string to handle bools, chars, and strings. + + It's also possible to provide implementation of toString for a class + which needs a string implementation. + */ + +template +std::string toString(T t) +{ + return std::to_string(t); +} + +inline std::string toString(bool b) +{ + return b ? "true" : "false"; +} + +inline std::string toString(char c) +{ + return std::string(1, c); +} + +inline std::string toString(std::string s) +{ + return s; +} + +inline std::string toString(char const* s) +{ + return s; +} + +namespace detail { + +// ConcatArg is used to represent arguments to stringConcat. + +struct ConcatArg { + ConcatArg(std::string const& s) : data_(s.data()), size_(s.size()) + { + } + + ConcatArg(char const* s) : data_(s), size_(strlen(s)) + { + } + + template + ConcatArg(T t) : string_(toString(t)), + data_(string_.data()), + size_(string_.size()) + { + } + + std::string string_; + char const* data_; + std::size_t size_; +}; + +} // namespace detail + +/** Concatenate strings, numbers, bools and chars into one string in O(n) time. + + Usage: + stringConcat({"hello ", 23, 'x', true}); + + Returns: + "hello 23xtrue" + */ +inline std::string stringConcat(std::vector args) +{ + int capacity = 0; + for (auto const& a: args) + capacity += a.size_; + + std::string result; + result.reserve(capacity); + for (auto const& a: args) + result.insert(result.end(), a.data_, a.data_ + a.size_); + return result; +} + } // ripple #endif