Remove SimpleStringBuilder and move StringBuilder impl to cc - Removed deprecated SimpleStringBuilder class. - Moved StringBuilder implementation to CC file. - Updated documentation to recommend StringBuilder. Some benefits of moving parts of the implementation to the cc file: - Help with enforcing correct build dependencies (including in downstream code) - Moves into the direction of making absl::StrCat an implementation detail of StringBuilder (helps with iwyu). This CL doesn't remove the include though for other reasons, just a step. - Small binary size benefits. Bug: none Change-Id: I4b9efdba45c3c4923233188ff38476e0ca2257c1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/466380 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47544}
diff --git a/g3doc/abseil-in-webrtc.md b/g3doc/abseil-in-webrtc.md index e824447..b21bc3c 100644 --- a/g3doc/abseil-in-webrtc.md +++ b/g3doc/abseil-in-webrtc.md
@@ -83,7 +83,7 @@ ### `absl::StrCat`, `absl::StrAppend`, `absl::StrJoin`, `absl::StrSplit` -*Use `webrtc::SimpleStringBuilder` to build strings.* +*Use `webrtc::StringBuilder` to build strings.* These are optimized for speed, not binary size. Even `StrCat` calls with a modest number of arguments can easily add several hundred bytes to the binary.
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 3850f30..abbc798 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn
@@ -625,6 +625,7 @@ deps = [ ":checks", ":safe_minmax", + "system:rtc_export", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view",
diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc index 8d12167..6f31b83 100644 --- a/rtc_base/strings/string_builder.cc +++ b/rtc_base/strings/string_builder.cc
@@ -13,98 +13,60 @@ #include <cstdarg> #include <cstdio> #include <cstring> -#include <span> +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "rtc_base/checks.h" -#include "rtc_base/numerics/safe_minmax.h" namespace webrtc { -SimpleStringBuilder::SimpleStringBuilder(std::span<char> buffer) - : buffer_(buffer) { - buffer_[0] = '\0'; - RTC_CHECK(IsConsistent()); -} - -SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { - return operator<<(absl::string_view(&ch, 1)); -} - -SimpleStringBuilder& SimpleStringBuilder::operator<<(absl::string_view str) { - RTC_CHECK_LT(size_ + str.length(), buffer_.size()) - << "Buffer size was insufficient"; - const size_t chars_added = SafeMin(str.length(), buffer_.size() - size_ - 1); - memcpy(&buffer_[size_], str.data(), chars_added); - size_ += chars_added; - buffer_[size_] = '\0'; - RTC_CHECK(IsConsistent()); +StringBuilder& StringBuilder::operator<<(const absl::string_view str) { + str_.append(str.data(), str.length()); return *this; } -// Numeric conversion routines. -// -// We use std::[v]snprintf instead of std::to_string because: -// * std::to_string relies on the current locale for formatting purposes, -// and therefore concurrent calls to std::to_string from multiple threads -// may result in partial serialization of calls -// * snprintf allows us to print the number directly into our buffer. -// * avoid allocating a std::string (potential heap alloc). -SimpleStringBuilder& SimpleStringBuilder::operator<<(int i) { - return AppendFormat("%d", i); +StringBuilder& StringBuilder::operator<<(char c) { + str_ += c; + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(unsigned i) { - return AppendFormat("%u", i); +StringBuilder& StringBuilder::operator<<(int i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(long i) { // NOLINT - return AppendFormat("%ld", i); +StringBuilder& StringBuilder::operator<<(unsigned i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(long long i) { // NOLINT - return AppendFormat("%lld", i); +StringBuilder& StringBuilder::operator<<(long i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<( - unsigned long i) { // NOLINT - return AppendFormat("%lu", i); +StringBuilder& StringBuilder::operator<<(long long i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<( - unsigned long long i) { // NOLINT - return AppendFormat("%llu", i); +StringBuilder& StringBuilder::operator<<(unsigned long i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(float f) { - return AppendFormat("%g", f); +StringBuilder& StringBuilder::operator<<(unsigned long long i) { + str_ += absl::StrCat(i); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(double f) { - return AppendFormat("%g", f); +StringBuilder& StringBuilder::operator<<(float f) { + str_ += absl::StrCat(f); + return *this; } -SimpleStringBuilder& SimpleStringBuilder::operator<<(long double f) { - return AppendFormat("%Lg", f); -} - -SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - const int len = - std::vsnprintf(&buffer_[size_], buffer_.size() - size_, fmt, args); - if (len >= 0) { - const size_t chars_added = SafeMin(len, buffer_.size() - 1 - size_); - size_ += chars_added; - RTC_CHECK_EQ(len, chars_added) << "Buffer size was insufficient"; - } else { - // This should never happen, but we're paranoid, so re-write the - // terminator in case vsnprintf() overwrote it. - RTC_DCHECK_NOTREACHED(); - buffer_[size_] = '\0'; - } - va_end(args); - RTC_CHECK(IsConsistent()); +StringBuilder& StringBuilder::operator<<(double f) { + str_ += absl::StrCat(f); return *this; }
diff --git a/rtc_base/strings/string_builder.h b/rtc_base/strings/string_builder.h index a3e0f0a..5254626d 100644 --- a/rtc_base/strings/string_builder.h +++ b/rtc_base/strings/string_builder.h
@@ -12,129 +12,36 @@ #define RTC_BASE_STRINGS_STRING_BUILDER_H_ #include <cstdio> -#include <span> #include <string> #include <utility> #include "absl/strings/has_absl_stringify.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "rtc_base/system/rtc_export.h" namespace webrtc { -// This is a minimalistic string builder class meant to cover the most cases of -// when you might otherwise be tempted to use a stringstream (discouraged for -// anything except logging). It uses a fixed-size buffer provided by the caller -// and concatenates strings and numbers into it, allowing the results to be -// read via `str()`. -class [[deprecated("Use webrtc::StringBuilder instead")]] SimpleStringBuilder { - public: - explicit SimpleStringBuilder(std::span<char> buffer); - SimpleStringBuilder(const SimpleStringBuilder&) = delete; - SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; - - SimpleStringBuilder& operator<<(char ch); - SimpleStringBuilder& operator<<(absl::string_view str); - SimpleStringBuilder& operator<<(int i); - SimpleStringBuilder& operator<<(unsigned i); - SimpleStringBuilder& operator<<(long i); // NOLINT - SimpleStringBuilder& operator<<(long long i); // NOLINT - SimpleStringBuilder& operator<<(unsigned long i); // NOLINT - SimpleStringBuilder& operator<<(unsigned long long i); // NOLINT - SimpleStringBuilder& operator<<(float f); - SimpleStringBuilder& operator<<(double f); - SimpleStringBuilder& operator<<(long double f); - - template <typename T> - requires absl::HasAbslStringify<T>::value - SimpleStringBuilder& operator<<(const T& value) { - return *this << absl::StrCat(value); - } - - // Returns a pointer to the built string. The name `str()` is borrowed for - // compatibility reasons as we replace usage of stringstream throughout the - // code base. - const char* str() const { return buffer_.data(); } - - // Returns the length of the string. The name `size()` is picked for STL - // compatibility reasons. - size_t size() const { return size_; } - -// Allows appending a printf style formatted string. -#if defined(__GNUC__) - __attribute__((__format__(__printf__, 2, 3))) -#endif - SimpleStringBuilder& - AppendFormat(const char* fmt, ...); - - private: - bool IsConsistent() const { - return size_ <= buffer_.size() - 1 && buffer_[size_] == '\0'; - } - - const std::span<char> buffer_; - 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. -class StringBuilder { +class RTC_EXPORT StringBuilder { public: StringBuilder() = default; explicit StringBuilder(absl::string_view s) : str_(s) {} StringBuilder(const StringBuilder&) = default; StringBuilder& operator=(const StringBuilder&) = default; - StringBuilder& operator<<(const absl::string_view str) { - str_.append(str.data(), str.length()); - return *this; - } - - StringBuilder& operator<<(char c) { - str_ += c; - return *this; - } - - StringBuilder& operator<<(int i) { - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(unsigned i) { - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(long i) { // NOLINT - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(long long i) { // NOLINT - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(unsigned long i) { // NOLINT - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(unsigned long long i) { // NOLINT - str_ += absl::StrCat(i); - return *this; - } - - StringBuilder& operator<<(float f) { - str_ += absl::StrCat(f); - return *this; - } - - StringBuilder& operator<<(double f) { - str_ += absl::StrCat(f); - return *this; - } + StringBuilder& operator<<(absl::string_view str); + StringBuilder& operator<<(char c); + StringBuilder& operator<<(int i); + StringBuilder& operator<<(unsigned i); + StringBuilder& operator<<(long i); // NOLINT + StringBuilder& operator<<(long long i); // NOLINT + StringBuilder& operator<<(unsigned long i); // NOLINT + StringBuilder& operator<<(unsigned long long i); // NOLINT + StringBuilder& operator<<(float f); + StringBuilder& operator<<(double f); template <typename T> requires absl::HasAbslStringify<T>::value
diff --git a/rtc_base/strings/string_builder_unittest.cc b/rtc_base/strings/string_builder_unittest.cc index 185ff1a..e8222f8 100644 --- a/rtc_base/strings/string_builder_unittest.cc +++ b/rtc_base/strings/string_builder_unittest.cc
@@ -33,104 +33,7 @@ std::string value_; }; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -TEST(SimpleStringBuilder, Limit) { - char sb_buf[10]; - SimpleStringBuilder sb(sb_buf); - EXPECT_EQ(0u, strlen(sb.str())); - - // Test that for a SSB with a buffer size of 10, that we can write 9 chars - // into it. - sb << "012345678"; // 9 characters + '\0'. - EXPECT_EQ(0, strcmp(sb.str(), "012345678")); -} - -TEST(SimpleStringBuilder, NumbersAndChars) { - char sb_buf[100]; - SimpleStringBuilder sb(sb_buf); - sb << 1 << ':' << 2.1 << ":" << 2.2f << ':' << 78187493520ll << ':' - << 78187493520ul; - EXPECT_EQ(0, strcmp(sb.str(), "1:2.1:2.2:78187493520:78187493520")); -} - -TEST(SimpleStringBuilder, Format) { - char sb_buf[100]; - SimpleStringBuilder sb(sb_buf); - sb << "Here we go - "; - sb.AppendFormat("This is a hex formatted value: 0x%08llx", 3735928559ULL); - EXPECT_EQ(0, - strcmp(sb.str(), - "Here we go - This is a hex formatted value: 0xdeadbeef")); -} - -TEST(SimpleStringBuilder, StdString) { - char sb_buf[100]; - SimpleStringBuilder sb(sb_buf); - std::string str = "does this work?"; - sb << str; - EXPECT_EQ(str, sb.str()); -} - -TEST(SimpleStringBuilder, CanUseAbslStringForCustomTypes) { - char sb_buf[100]; - SimpleStringBuilder sb(sb_buf); - StructWithAbslStringify value("absl-stringify"); - sb << value; - EXPECT_STREQ(sb.str(), "absl-stringify"); -} - -#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) - -TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharP) { - char sb_buf[4]; - SimpleStringBuilder sb(sb_buf); - const char* const msg = "This is just too much"; - EXPECT_DEATH(sb << msg, ""); -} - -TEST(SimpleStringBuilderDeathTest, BufferOverrunStdString) { - char sb_buf[4]; - SimpleStringBuilder sb(sb_buf); - sb << 12; - const std::string msg = "Aw, come on!"; - EXPECT_DEATH(sb << msg, ""); -} - -TEST(SimpleStringBuilderDeathTest, BufferOverrunInt) { - char sb_buf[4]; - SimpleStringBuilder sb(sb_buf); - constexpr int num = -12345; - EXPECT_DEATH(sb << num, ""); -} - -TEST(SimpleStringBuilderDeathTest, BufferOverrunDouble) { - char sb_buf[5]; - SimpleStringBuilder sb(sb_buf); - constexpr double num = 123.456; - EXPECT_DEATH(sb << num, ""); -} - -TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharPAlreadyFull) { - char sb_buf[4]; - SimpleStringBuilder sb(sb_buf); - sb << 123; - const char* const msg = "This is just too much"; - EXPECT_DEATH(sb << msg, ""); -} - -TEST(SimpleStringBuilderDeathTest, BufferOverrunIntAlreadyFull) { - char sb_buf[4]; - SimpleStringBuilder sb(sb_buf); - sb << "xyz"; - constexpr int num = -12345; - EXPECT_DEATH(sb << num, ""); -} - -#endif - -#pragma clang diagnostic pop //////////////////////////////////////////////////////////////////////////////// // StringBuilder.