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:
parent
3611afc1c1
commit
88e1848fd5
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user