Add a StringBuilder class.

String builder is similar to SimpleStringBuilder, but the difference is
that StringBuilder is built around a std::string instance and supports
dynamic resizing.

Change-Id: I874d22e69e639ff9ef3d5929366f4ba71c545787
Bug: webrtc:8982
Reviewed-on: https://webrtc-review.googlesource.com/58980
Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24526}
This commit is contained in:
Jonas Olsson 2018-09-03 10:15:08 +02:00 committed by Commit Bot
parent 3611afc1c1
commit 88e1848fd5
6 changed files with 178 additions and 0 deletions

View File

@ -328,6 +328,7 @@ rtc_source_set("stringutils") {
":macromagic",
":safe_minmax",
"../api:array_view",
"//third_party/abseil-cpp/absl/strings:strings",
"//third_party/abseil-cpp/absl/types:optional",
]
}

View File

@ -478,6 +478,13 @@ std::string ToString(const double d) {
return std::string(&buf[0], len);
}
std::string ToString(const long double d) {
char buf[32];
const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d);
RTC_DCHECK_LE(len, arraysize(buf));
return std::string(&buf[0], len);
}
std::string ToString(const void* const p) {
char buf[32];
const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p);

View File

@ -164,6 +164,7 @@ std::string ToString(long long int s);
std::string ToString(unsigned long long int s);
std::string ToString(double t);
std::string ToString(long double t);
std::string ToString(const void* p);

View File

@ -109,4 +109,24 @@ SimpleStringBuilder& SimpleStringBuilder::Append(const char* str,
return *this;
}
StringBuilder& StringBuilder::AppendFormat(const char* fmt, ...) {
va_list args, copy;
va_start(args, fmt);
va_copy(copy, args);
const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy);
va_end(copy);
RTC_DCHECK_GE(predicted_length, 0);
if (predicted_length > 0) {
const size_t size = str_.size();
str_.resize(size + predicted_length);
// Pass "+ 1" to vsnprintf to include space for the '\0'.
const int actual_length =
std::vsnprintf(&str_[size], predicted_length + 1, fmt, args);
RTC_DCHECK_GE(actual_length, 0);
}
va_end(args);
return *this;
}
} // namespace rtc

View File

@ -15,9 +15,11 @@
#include <cstring>
#include <string>
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/stringencode.h"
#include "rtc_base/stringutils.h"
namespace rtc {
@ -82,6 +84,95 @@ class SimpleStringBuilder {
size_t size_ = 0;
};
// A string builder that supports dynamic resizing while building a string.
// The class is based around an instance of std::string and allows moving
// ownership out of the class once the string has been built.
// Note that this class uses the heap for allocations, so SimpleStringBuilder
// might be more efficient for some use cases.
class StringBuilder {
public:
StringBuilder() {}
explicit StringBuilder(absl::string_view s) : str_(s) {}
// TODO(tommi): Support construction from StringBuilder?
StringBuilder(const StringBuilder&) = delete;
StringBuilder& operator=(const StringBuilder&) = delete;
StringBuilder& operator<<(const absl::string_view str) {
str_.append(str.data(), str.length());
return *this;
}
StringBuilder& operator<<(char c) = delete;
StringBuilder& operator<<(int i) {
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(unsigned i) {
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(long i) { // NOLINT
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(long long i) { // NOLINT
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(unsigned long i) { // NOLINT
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(unsigned long long i) { // NOLINT
str_ += rtc::ToString(i);
return *this;
}
StringBuilder& operator<<(float f) {
str_ += rtc::ToString(f);
return *this;
}
StringBuilder& operator<<(double f) {
str_ += rtc::ToString(f);
return *this;
}
StringBuilder& operator<<(long double f) {
str_ += rtc::ToString(f);
return *this;
}
const std::string& str() const { return str_; }
void Clear() { str_.clear(); }
size_t size() const { return str_.size(); }
std::string Release() {
std::string ret;
std::swap(str_, ret);
return ret;
}
// Allows appending a printf style formatted string.
StringBuilder& AppendFormat(const char* fmt, ...)
#if defined(__GNUC__)
__attribute__((__format__(__printf__, 2, 3)))
#endif
;
private:
std::string str_;
};
} // namespace rtc
#endif // RTC_BASE_STRINGS_STRING_BUILDER_H_

View File

@ -140,4 +140,62 @@ TEST(SimpleStringBuilder, BufferOverrunIntAlreadyFull) {
#endif
////////////////////////////////////////////////////////////////////////////////
// StringBuilder.
TEST(StringBuilder, Limit) {
StringBuilder sb;
EXPECT_EQ(0u, sb.str().size());
sb << "012345678";
EXPECT_EQ(sb.str(), "012345678");
}
TEST(StringBuilder, NumbersAndChars) {
StringBuilder sb;
sb << 1 << ":" << 2.1 << ":" << 2.2f << ":" << 78187493520ll << ":"
<< 78187493520ul;
EXPECT_THAT(sb.str(),
testing::MatchesRegex("1:2.10*:2.20*:78187493520:78187493520"));
}
TEST(StringBuilder, Format) {
StringBuilder sb;
sb << "Here we go - ";
sb.AppendFormat("This is a hex formatted value: 0x%08llx", 3735928559ULL);
EXPECT_EQ(sb.str(), "Here we go - This is a hex formatted value: 0xdeadbeef");
}
TEST(StringBuilder, StdString) {
StringBuilder sb;
std::string str = "does this work?";
sb << str;
EXPECT_EQ(str, sb.str());
}
TEST(StringBuilder, Release) {
StringBuilder sb;
std::string str =
"This string has to be of a moderate length, or we might "
"run into problems with small object optimizations.";
EXPECT_LT(sizeof(str), str.size());
sb << str;
EXPECT_EQ(str, sb.str());
const char* original_buffer = sb.str().c_str();
std::string moved = sb.Release();
EXPECT_TRUE(sb.str().empty());
EXPECT_EQ(str, moved);
EXPECT_EQ(original_buffer, moved.c_str());
}
TEST(StringBuilder, Reset) {
StringBuilder sb("abc");
sb << "def";
EXPECT_EQ("abcdef", sb.str());
sb.Clear();
EXPECT_TRUE(sb.str().empty());
sb << 123 << "!";
EXPECT_EQ("123!", sb.str());
}
} // namespace rtc