Replace use of SimpleStringBuilder with StringBuilder

Deprecate the SimpleStringBuilder class and migrate all existing usages
to StringBuilder in webrtc. This makes string construction across the
codebase more consistent and eliminates the need for manually manage
fixed-size buffers, which reduces boilerplate and mitigates the risk of
buffer overflows.

Key modifications:
* Mark the SimpleStringBuilder implementation as deprecated.
* Update StringBuilder to support appending `char` via operator<<.
* Refactor multiple implementation details — including API, audio,
  video, and test utilities—to use StringBuilder.
* Update function signatures to accept StringBuilder pointers or
  references where applicable.
* Simplified string generation in various ToString() methods by
  removing local character array buffers and use .Release() instead
  of .str() where appropriate.

Bug: webrtc:500767594
Change-Id: I738a78a176c17242e51190a4ac073080787d3d24
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/462202
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47374}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 60a78a5..9abb3de 100755
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -536,7 +536,7 @@
         'std::ostream& F() {  // no-presubmit-check TODO(webrtc:8982)\n'
         '\n'
         'If you are adding new code, consider using '
-        'webrtc::SimpleStringBuilder\n'
+        'webrtc::StringBuilder\n'
         '(in rtc_base/strings/string_builder.h).\n'
         'Affected files:\n')
     errors = []  # 2-element tuples with (file, line number)
diff --git a/api/audio/audio_device_defines.h b/api/audio/audio_device_defines.h
index 63eca2a..8976eca 100644
--- a/api/audio/audio_device_defines.h
+++ b/api/audio/audio_device_defines.h
@@ -153,8 +153,7 @@
     return static_cast<double>(frames_per_buffer_) / (sample_rate_);
   }
   std::string ToString() const {
-    char ss_buf[1024];
-    SimpleStringBuilder ss(ss_buf);
+    StringBuilder ss;
     ss << "AudioParameters: ";
     ss << "sample_rate=" << sample_rate() << ", channels=" << channels();
     ss << ", frames_per_buffer=" << frames_per_buffer();
@@ -163,7 +162,7 @@
     ss << ", bytes_per_buffer=" << GetBytesPerBuffer();
     ss << ", bytes_per_10ms_buffer=" << GetBytesPer10msBuffer();
     ss << ", size_in_ms=" << GetBufferSizeInMilliseconds();
-    return ss.str();
+    return ss.Release();
   }
 
  private:
diff --git a/api/audio/audio_processing.cc b/api/audio/audio_processing.cc
index bf0af03..5a4018a 100644
--- a/api/audio/audio_processing.cc
+++ b/api/audio/audio_processing.cc
@@ -125,8 +125,7 @@
 }
 
 std::string AudioProcessing::Config::ToString() const {
-  char buf[2048];
-  SimpleStringBuilder builder(buf);
+  StringBuilder builder;
   builder << "AudioProcessing::Config{ "
              "pipeline: { "
              "maximum_internal_processing_rate: "
@@ -208,7 +207,7 @@
           << gain_controller2.adaptive_digital.max_output_noise_level_dbfs
           << " }, input_volume_control : { enabled "
           << gain_controller2.input_volume_controller.enabled << "}}";
-  return builder.str();
+  return builder.Release();
 }
 
 absl_nonnull std::unique_ptr<AudioProcessingBuilderInterface>
diff --git a/api/audio_options.cc b/api/audio_options.cc
index 8342920..4dca717 100644
--- a/api/audio_options.cc
+++ b/api/audio_options.cc
@@ -19,7 +19,7 @@
 namespace {
 
 template <class T>
-void ToStringIfSet(SimpleStringBuilder* result,
+void ToStringIfSet(StringBuilder* result,
                    const char* key,
                    const std::optional<T>& val) {
   if (val) {
@@ -79,8 +79,7 @@
 }
 
 std::string AudioOptions::ToString() const {
-  char buffer[1024];
-  SimpleStringBuilder result(buffer);
+  StringBuilder result;
   result << "AudioOptions {";
   ToStringIfSet(&result, "aec", echo_cancellation);
 #if defined(WEBRTC_IOS)
@@ -100,7 +99,7 @@
   ToStringIfSet(&result, "audio_network_adaptor", audio_network_adaptor);
   ToStringIfSet(&result, "init_recording_on_send", init_recording_on_send);
   result << "}";
-  return result.str();
+  return result.Release();
 }
 
 }  // namespace webrtc
diff --git a/api/neteq/neteq.cc b/api/neteq/neteq.cc
index df903a8..ca4941d 100644
--- a/api/neteq/neteq.cc
+++ b/api/neteq/neteq.cc
@@ -24,15 +24,14 @@
 NetEq::Config& NetEq::Config::operator=(Config&&) = default;
 
 std::string NetEq::Config::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "sample_rate_hz=" << sample_rate_hz
      << ", max_packets_in_buffer=" << max_packets_in_buffer
      << ", min_delay_ms=" << min_delay_ms << ", enable_fast_accelerate="
      << (enable_fast_accelerate ? "true" : "false")
      << ", enable_muted_state=" << (enable_muted_state ? "true" : "false")
      << ", enable_rtx_handling=" << (enable_rtx_handling ? "true" : "false");
-  return ss.str();
+  return ss.Release();
 }
 
 }  // namespace webrtc
diff --git a/api/units/data_rate.cc b/api/units/data_rate.cc
index 5b3838f..f3193b6 100644
--- a/api/units/data_rate.cc
+++ b/api/units/data_rate.cc
@@ -17,8 +17,7 @@
 namespace webrtc {
 
 std::string ToString(DataRate value) {
-  char buf[64];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   if (value.IsPlusInfinity()) {
     sb << "+inf bps";
   } else if (value.IsMinusInfinity()) {
@@ -30,6 +29,6 @@
       sb << value.kbps() << " kbps";
     }
   }
-  return sb.str();
+  return sb.Release();
 }
 }  // namespace webrtc
diff --git a/api/units/data_size.cc b/api/units/data_size.cc
index 24bb83c..0d57500 100644
--- a/api/units/data_size.cc
+++ b/api/units/data_size.cc
@@ -17,8 +17,7 @@
 namespace webrtc {
 
 std::string ToString(DataSize value) {
-  char buf[64];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   if (value.IsPlusInfinity()) {
     sb << "+inf bytes";
   } else if (value.IsMinusInfinity()) {
@@ -26,6 +25,6 @@
   } else {
     sb << value.bytes() << " bytes";
   }
-  return sb.str();
+  return sb.Release();
 }
 }  // namespace webrtc
diff --git a/api/units/frequency.cc b/api/units/frequency.cc
index ca4b52e..d796125 100644
--- a/api/units/frequency.cc
+++ b/api/units/frequency.cc
@@ -16,8 +16,7 @@
 
 namespace webrtc {
 std::string ToString(Frequency value) {
-  char buf[64];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   if (value.IsPlusInfinity()) {
     sb << "+inf Hz";
   } else if (value.IsMinusInfinity()) {
@@ -27,6 +26,6 @@
   } else {
     sb << value.hertz<int64_t>() << " Hz";
   }
-  return sb.str();
+  return sb.Release();
 }
 }  // namespace webrtc
diff --git a/api/units/time_delta.cc b/api/units/time_delta.cc
index 9e1521b..c267739 100644
--- a/api/units/time_delta.cc
+++ b/api/units/time_delta.cc
@@ -17,8 +17,7 @@
 namespace webrtc {
 
 std::string ToString(TimeDelta value) {
-  char buf[64];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   if (value.IsPlusInfinity()) {
     sb << "+inf ms";
   } else if (value.IsMinusInfinity()) {
@@ -31,7 +30,7 @@
     else
       sb << value.seconds() << " s";
   }
-  return sb.str();
+  return sb.Release();
 }
 
 }  // namespace webrtc
diff --git a/api/units/timestamp.cc b/api/units/timestamp.cc
index 6dd64ab..41d12e6 100644
--- a/api/units/timestamp.cc
+++ b/api/units/timestamp.cc
@@ -16,8 +16,7 @@
 
 namespace webrtc {
 std::string ToString(Timestamp value) {
-  char buf[64];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   if (value.IsPlusInfinity()) {
     sb << "+inf ms";
   } else if (value.IsMinusInfinity()) {
@@ -30,6 +29,6 @@
     else
       sb << value.seconds() << " s";
   }
-  return sb.str();
+  return sb.Release();
 }
 }  // namespace webrtc
diff --git a/api/video/color_space.cc b/api/video/color_space.cc
index 776cd12..7994d85 100644
--- a/api/video/color_space.cc
+++ b/api/video/color_space.cc
@@ -138,8 +138,7 @@
     break;
 
 std::string ColorSpace::AsString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{primaries:";
   switch (primaries_) {
     PRINT_ENUM_CASE(PrimaryID, kBT709)
@@ -201,7 +200,7 @@
     PRINT_ENUM_CASE(RangeID, kDerived)
   }
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 #undef PRINT_ENUM_CASE
diff --git a/api/video/video_bitrate_allocation.cc b/api/video/video_bitrate_allocation.cc
index 98735cb..e5463873 100644
--- a/api/video/video_bitrate_allocation.cc
+++ b/api/video/video_bitrate_allocation.cc
@@ -146,8 +146,7 @@
 
   // Max string length in practice is 260, but let's have some overhead and
   // round up to nearest power of two.
-  char string_buf[512];
-  SimpleStringBuilder ssb(string_buf);
+  StringBuilder ssb;
 
   ssb << "VideoBitrateAllocation [";
   uint32_t spatial_cumulator = 0;
@@ -184,7 +183,7 @@
 
   RTC_DCHECK_EQ(spatial_cumulator, sum_);
   ssb << " ]";
-  return ssb.str();
+  return ssb.Release();
 }
 
 }  // namespace webrtc
diff --git a/api/video/video_timing.cc b/api/video/video_timing.cc
index bc9ee80..fcb2e88 100644
--- a/api/video/video_timing.cc
+++ b/api/video/video_timing.cc
@@ -87,8 +87,7 @@
     return "";
   }
 
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
 
   sb << rtp_timestamp << ',' << capture_time_ms << ',' << encode_start_ms << ','
      << encode_finish_ms << ',' << packetization_finish_ms << ','
@@ -98,7 +97,7 @@
      << ',' << render_time_ms << ',' << IsOutlier() << ','
      << IsTimerTriggered();
 
-  return sb.str();
+  return sb.Release();
 }
 
 VideoPlayoutDelay::VideoPlayoutDelay(TimeDelta min, TimeDelta max)
diff --git a/api/video_codecs/video_codec.cc b/api/video_codecs/video_codec.cc
index b5bdc66..dc02cc4 100644
--- a/api/video_codecs/video_codec.cc
+++ b/api/video_codecs/video_codec.cc
@@ -78,8 +78,7 @@
       complexity_(VideoCodecComplexity::kComplexityNormal) {}
 
 std::string VideoCodec::ToString() const {
-  char string_buf[2048];
-  SimpleStringBuilder ss(string_buf);
+  StringBuilder ss;
 
   ss << "VideoCodec {" << "type: " << CodecTypeToPayloadString(codecType)
      << ", mode: "
@@ -100,7 +99,7 @@
     ss << "}";
   }
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 VideoCodecVP8* VideoCodec::VP8() {
diff --git a/api/video_codecs/video_decoder.cc b/api/video_codecs/video_decoder.cc
index 84f3652..ec09c0a 100644
--- a/api/video_codecs/video_decoder.cc
+++ b/api/video_codecs/video_decoder.cc
@@ -43,14 +43,13 @@
 }
 
 std::string VideoDecoder::DecoderInfo::ToString() const {
-  char string_buf[2048];
-  SimpleStringBuilder oss(string_buf);
+  StringBuilder oss;
 
   oss << "DecoderInfo { "
       << "prefers_late_decoding = " << "implementation_name = '"
       << implementation_name << "', " << "is_hardware_accelerated = "
       << (is_hardware_accelerated ? "true" : "false") << " }";
-  return oss.str();
+  return oss.Release();
 }
 
 bool VideoDecoder::DecoderInfo::operator==(const DecoderInfo& rhs) const {
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 4f052c6..02ee152 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -52,8 +52,7 @@
 namespace webrtc {
 
 std::string AudioReceiveStreamInterface::Config::Rtp::ToString() const {
-  char ss_buf[1024];
-  SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   ss << "{remote_ssrc: " << remote_ssrc;
   ss << ", nack: " << nack.ToString();
   ss << ", rtcp: "
@@ -61,12 +60,11 @@
              ? "compound"
              : (rtcp_mode == RtcpMode::kReducedSize ? "reducedSize" : "off"));
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 std::string AudioReceiveStreamInterface::Config::ToString() const {
-  char ss_buf[1024];
-  SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   ss << "{rtp: " << rtp.ToString();
   ss << ", rtcp_send_transport: "
      << (rtcp_send_transport ? "(Transport)" : "null");
@@ -74,7 +72,7 @@
     ss << ", sync_group: " << sync_group;
   }
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 namespace {
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index dfd4cd4..a6866fa 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -759,8 +759,7 @@
   if (frame_transformer_delegate_) {
     // Asynchronously transform the received payload. After the payload is
     // transformed, the delegate will call OnReceivedPayloadData to handle it.
-    char buf[1024];
-    SimpleStringBuilder mime_type(buf);
+    StringBuilder mime_type;
     auto it = payload_type_map_.find(header.payloadType);
     mime_type << MediaTypeToString(MediaType::AUDIO) << "/"
               << (it != payload_type_map_.end() ? it->second.name
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 528bd26..4b5264c 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -391,8 +391,7 @@
   if (frame_transformer_delegate_) {
     // Asynchronously transform the payload before sending it. After the payload
     // is transformed, the delegate will call SendRtpAudio to send it.
-    char buf[1024];
-    SimpleStringBuilder mime_type(buf);
+    StringBuilder mime_type;
     mime_type << MediaTypeToString(MediaType::AUDIO) << "/"
               << encoder_format_.name;
     frame_transformer_delegate_->Transform(
diff --git a/call/audio_send_stream.cc b/call/audio_send_stream.cc
index dc41a6a..94bfe16 100644
--- a/call/audio_send_stream.cc
+++ b/call/audio_send_stream.cc
@@ -49,8 +49,7 @@
 AudioSendStream::Config::Rtp::~Rtp() = default;
 
 std::string AudioSendStream::Config::Rtp::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ssrc: " << ssrc;
   if (!rid.empty()) {
     ss << ", rid: " << rid;
@@ -77,7 +76,7 @@
   ss << ']';
   ss << ", c_name: " << c_name;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 AudioSendStream::Config::SendCodecSpec::SendCodecSpec(
@@ -87,8 +86,7 @@
 AudioSendStream::Config::SendCodecSpec::~SendCodecSpec() = default;
 
 std::string AudioSendStream::Config::SendCodecSpec::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{nack_enabled: " << (nack_enabled ? "true" : "false");
   ss << ", enable_non_sender_rtt: "
      << (enable_non_sender_rtt ? "true" : "false");
@@ -99,7 +97,7 @@
   ss << ", payload_type: " << payload_type;
   ss << ", format: " << absl::StrCat(format);
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 bool AudioSendStream::Config::SendCodecSpec::operator==(
diff --git a/call/call.cc b/call/call.cc
index df8786c8..0efef93 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -483,8 +483,7 @@
 }  // namespace internal
 
 std::string Call::Stats::ToString(int64_t time_ms) const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "Call stats: " << time_ms << ", {";
   ss << "send_bw_bps: " << send_bandwidth_bps << ", ";
   ss << "recv_bw_bps: " << recv_bandwidth_bps << ", ";
@@ -492,7 +491,7 @@
   ss << "pacer_delay_ms: " << pacer_delay_ms << ", ";
   ss << "rtt_ms: " << rtt_ms;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 std::unique_ptr<Call> Call::Create(CallConfig config) {
diff --git a/call/flexfec_receive_stream_impl.cc b/call/flexfec_receive_stream_impl.cc
index d694d06..9e4431b 100644
--- a/call/flexfec_receive_stream_impl.cc
+++ b/call/flexfec_receive_stream_impl.cc
@@ -31,8 +31,7 @@
 namespace webrtc {
 
 std::string FlexfecReceiveStream::Config::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{payload_type: " << payload_type;
   ss << ", remote_ssrc: " << remote_ssrc;
   ss << ", protected_media_ssrcs: [";
@@ -42,7 +41,7 @@
   if (!protected_media_ssrcs.empty())
     ss << protected_media_ssrcs[i];
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 bool FlexfecReceiveStream::Config::IsCompleteAndEnabled() const {
diff --git a/call/rtp_config.cc b/call/rtp_config.cc
index f48ae30..560ec09 100644
--- a/call/rtp_config.cc
+++ b/call/rtp_config.cc
@@ -46,21 +46,19 @@
 }
 
 std::string NackConfig::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{rtp_history_ms: " << rtp_history_ms;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 std::string UlpfecConfig::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ulpfec_payload_type: " << ulpfec_payload_type;
   ss << ", red_payload_type: " << red_payload_type;
   ss << ", red_rtx_payload_type: " << red_rtx_payload_type;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 bool UlpfecConfig::operator==(const UlpfecConfig& other) const {
@@ -70,8 +68,7 @@
 }
 
 std::string RtpStreamConfig::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ssrc: " << ssrc;
   ss << ", rid: " << rid;
   ss << ", payload_name: " << payload_name;
@@ -81,16 +78,15 @@
     ss << ", rtx: " << rtx->ToString();
   }
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 std::string RtpStreamConfig::Rtx::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ssrc: " << ssrc;
   ss << ", payload_type: " << payload_type;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 RtpConfig::RtpConfig() = default;
@@ -102,8 +98,7 @@
 RtpConfig::Flexfec::~Flexfec() = default;
 
 std::string RtpConfig::ToString() const {
-  char buf[2 * 1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ssrcs: [";
   for (size_t i = 0; i < ssrcs.size(); ++i) {
     ss << ssrcs[i];
@@ -158,7 +153,7 @@
   ss << ", rtx: " << rtx.ToString();
   ss << ", c_name: " << c_name;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 RtpConfig::Rtx::Rtx() = default;
@@ -166,8 +161,7 @@
 RtpConfig::Rtx::~Rtx() = default;
 
 std::string RtpConfig::Rtx::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{ssrcs: [";
   for (size_t i = 0; i < ssrcs.size(); ++i) {
     ss << ssrcs[i];
@@ -178,7 +172,7 @@
 
   ss << ", payload_type: " << payload_type;
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 bool RtpConfig::IsMediaSsrc(uint32_t ssrc) const {
diff --git a/call/video_receive_stream.cc b/call/video_receive_stream.cc
index 5a1c168..959c5b4 100644
--- a/call/video_receive_stream.cc
+++ b/call/video_receive_stream.cc
@@ -50,7 +50,7 @@
   ss << "}";
   ss << "}";
 
-  return ss.str();
+  return ss.Release();
 }
 
 VideoReceiveStreamInterface::Stats::Stats() = default;
@@ -101,7 +101,7 @@
   ss << "firCount: " << rtcp_packet_type_counts.fir_packets << ", ";
   ss << "pliCount: " << rtcp_packet_type_counts.pli_packets;
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 VideoReceiveStreamInterface::Config::Config(const Config&) = default;
@@ -132,7 +132,7 @@
     ss << ", sync_group: " << sync_group;
   ss << "}";
 
-  return ss.str();
+  return ss.Release();
 }
 
 VideoReceiveStreamInterface::Config::Rtp::Rtp() = default;
@@ -165,7 +165,7 @@
   }
   ss << "}";
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 }  // namespace webrtc
diff --git a/call/video_send_stream.cc b/call/video_send_stream.cc
index e68f73b..e2d60a2 100644
--- a/call/video_send_stream.cc
+++ b/call/video_send_stream.cc
@@ -63,7 +63,7 @@
   ss << "nack: " << rtcp_packet_type_counts.nack_packets << ", ";
   ss << "fir: " << rtcp_packet_type_counts.fir_packets << ", ";
   ss << "pli: " << rtcp_packet_type_counts.pli_packets;
-  return ss.str();
+  return ss.Release();
 }
 
 VideoSendStream::Stats::Stats() = default;
@@ -97,7 +97,7 @@
       ss << "}";
     }
   }
-  return ss.str();
+  return ss.Release();
 }
 
 VideoSendStream::Config::Config(const Config&) = default;
@@ -122,7 +122,7 @@
   ss << ", suspend_below_min_bitrate: "
      << (suspend_below_min_bitrate ? "on" : "off");
   ss << "}";
-  return ss.str();
+  return ss.Release();
 }
 
 }  // namespace webrtc
diff --git a/g3doc/style-guide.md b/g3doc/style-guide.md
index eee3b46..51a4aea 100644
--- a/g3doc/style-guide.md
+++ b/g3doc/style-guide.md
@@ -149,7 +149,7 @@
 be verified whenever accepting external input.
 
 For concatenation of strings, use `webrtc::StrJoin` or
-`webrtc::SimpleStringBuilder` directly.
+`webrtc::StringBuilder` directly.
 
 For string views, use `absl::string_view`, not `std::string_view`. The former is
 heavily used in webrtc, and there are platforms we export to where the two are
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 535fcd8..d78a1a6 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -274,9 +274,7 @@
 }
 
 std::string Codec::ToString() const {
-  char buf[256];
-
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   switch (type) {
     case Type::kAudio: {
       sb << "AudioCodec[" << id << ":" << name << ":" << clockrate << ":"
@@ -292,7 +290,7 @@
       break;
     }
   }
-  return sb.str();
+  return sb.Release();
 }
 
 Codec CreateAudioRtxCodec(PayloadType rtx_payload_type,
diff --git a/media/base/stream_params.cc b/media/base/stream_params.cc
index d330d9c..d08baed 100644
--- a/media/base/stream_params.cc
+++ b/media/base/stream_params.cc
@@ -25,7 +25,7 @@
 namespace webrtc {
 namespace {
 
-void AppendSsrcs(std::span<const uint32_t> ssrcs, SimpleStringBuilder* sb) {
+void AppendSsrcs(std::span<const uint32_t> ssrcs, StringBuilder* sb) {
   *sb << "ssrcs:[";
   const char* delimiter = "";
   for (uint32_t ssrc : ssrcs) {
@@ -36,7 +36,7 @@
 }
 
 void AppendSsrcGroups(std::span<const SsrcGroup> ssrc_groups,
-                      SimpleStringBuilder* sb) {
+                      StringBuilder* sb) {
   *sb << "ssrc_groups:";
   const char* delimiter = "";
   for (const SsrcGroup& ssrc_group : ssrc_groups) {
@@ -46,7 +46,7 @@
 }
 
 void AppendStreamIds(std::span<const std::string> stream_ids,
-                     SimpleStringBuilder* sb) {
+                     StringBuilder* sb) {
   *sb << "stream_ids:";
   const char* delimiter = "";
   for (const std::string& stream_id : stream_ids) {
@@ -55,7 +55,7 @@
   }
 }
 
-void AppendRids(std::span<const RidDescription> rids, SimpleStringBuilder* sb) {
+void AppendRids(std::span<const RidDescription> rids, StringBuilder* sb) {
   *sb << "rids:[";
   const char* delimiter = "";
   for (const RidDescription& rid : rids) {
@@ -96,13 +96,12 @@
 }
 
 std::string SsrcGroup::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "{";
   sb << "semantics:" << semantics << ";";
   AppendSsrcs(ssrcs, &sb);
   sb << "}";
-  return sb.str();
+  return sb.Release();
 }
 
 StreamParams::StreamParams() = default;
@@ -121,8 +120,7 @@
 }
 
 std::string StreamParams::ToString() const {
-  char buf[2 * 1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "{";
   if (!id.empty()) {
     sb << "id:" << id << ";";
@@ -141,7 +139,7 @@
     sb << ";";
   }
   sb << "}";
-  return sb.str();
+  return sb.Release();
 }
 
 void StreamParams::GenerateSsrcs(int num_layers,
diff --git a/media/base/video_common.cc b/media/base/video_common.cc
index 38b5562..da2221a 100644
--- a/media/base/video_common.cc
+++ b/media/base/video_common.cc
@@ -70,11 +70,10 @@
     }
   }
 
-  char buf[256];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << fourcc_name << width << "x" << height << "x"
      << IntervalToFpsFloat(interval);
-  return sb.str();
+  return sb.Release();
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_getter.cc b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
index 4c3ced7..29a278f 100644
--- a/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
+++ b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
@@ -28,12 +28,11 @@
 namespace test {
 
 std::string NetEqStatsGetter::ConcealmentEvent::ToString() const {
-  char ss_buf[256];
-  SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   ss << "ConcealmentEvent duration_ms:" << duration_ms
      << " event_number:" << concealment_event_number
      << " time_from_previous_event_end_ms:" << time_from_previous_event_end_ms;
-  return ss.str();
+  return ss.Release();
 }
 
 NetEqStatsGetter::NetEqStatsGetter(
diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc
index e6e97bf..3b6bc31 100644
--- a/modules/audio_device/mac/audio_device_mac.cc
+++ b/modules/audio_device/mac/audio_device_mac.cc
@@ -1693,8 +1693,12 @@
     WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
         usedID, &propertyAddress, 0, NULL, &len, devName.data()));
 
-    SimpleStringBuilder ss(name);
+    StringBuilder ss;
     ss.AppendFormat("default (%s)", devName.data());
+    const std::string& s = ss.str();
+    size_t chars_to_copy = std::min(s.size(), name.size() - 1);
+    std::copy_n(s.data(), chars_to_copy, name.data());
+    name[chars_to_copy] = '\0';
   } else {
     if (index < numberDevices) {
       usedID = deviceIds[index];
diff --git a/modules/audio_device/win/core_audio_utility_win.cc b/modules/audio_device/win/core_audio_utility_win.cc
index dbfefa0..74a9c81 100644
--- a/modules/audio_device/win/core_audio_utility_win.cc
+++ b/modules/audio_device/win/core_audio_utility_win.cc
@@ -1476,8 +1476,7 @@
 }
 
 std::string WaveFormatToString(const WaveFormatWrapper format) {
-  char ss_buf[1024];
-  webrtc::SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   // Start with the WAVEFORMATEX part (which always exists).
   ss.AppendFormat("wFormatTag: %s (0x%X)",
                   WaveFormatTagToString(format->wFormatTag),
@@ -1489,7 +1488,7 @@
   ss.AppendFormat(", wBitsPerSample: %d", format->wBitsPerSample);
   ss.AppendFormat(", cbSize: %d", format->cbSize);
   if (!format.IsExtensible())
-    return ss.str();
+    return ss.Release();
 
   // Append the WAVEFORMATEXTENSIBLE part (which we know exists).
   ss.AppendFormat(
@@ -1503,7 +1502,7 @@
   } else {
     ss.AppendFormat("%s", ", SubFormat: NOT_SUPPORTED");
   }
-  return ss.str();
+  return ss.Release();
 }
 
 webrtc::TimeDelta ReferenceTimeToTimeDelta(REFERENCE_TIME time) {
@@ -1517,10 +1516,9 @@
 }
 
 std::string ErrorToString(const _com_error& error) {
-  char ss_buf[1024];
-  webrtc::SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   ss.AppendFormat("(HRESULT: 0x%08X)", error.Error());
-  return ss.str();
+  return ss.Release();
 }
 
 }  // namespace core_audio_utility
diff --git a/modules/audio_processing/aec3/block_delay_buffer_unittest.cc b/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
index 68a6db9..32f791b 100644
--- a/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
+++ b/modules/audio_processing/aec3/block_delay_buffer_unittest.cc
@@ -40,11 +40,10 @@
 }
 
 std::string ProduceDebugText(int sample_rate_hz, size_t delay) {
-  char log_stream_buffer[8 * 1024];
-  SimpleStringBuilder ss(log_stream_buffer);
+  StringBuilder ss;
   ss << "Sample rate: " << sample_rate_hz;
   ss << ", Delay: " << delay;
-  return ss.str();
+  return ss.Release();
 }
 
 }  // namespace
diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
index 2b45b57..a92e92a 100644
--- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc
+++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
@@ -161,8 +161,7 @@
     "WebRTC-Audio-2ndAgcMinMicLevelExperiment";
 
 std::string GetAgcMinMicLevelExperimentFieldTrial(const std::string& value) {
-  char field_trial_buffer[64];
-  SimpleStringBuilder builder(field_trial_buffer);
+  StringBuilder builder;
   builder << kMinMicLevelFieldTrial << "/" << value << "/";
   return builder.str();
 }
@@ -172,8 +171,7 @@
     const std::string& suffix = "") {
   RTC_DCHECK_GE(enabled_value, 0);
   RTC_DCHECK_LE(enabled_value, 255);
-  char field_trial_buffer[64];
-  SimpleStringBuilder builder(field_trial_buffer);
+  StringBuilder builder;
   builder << kMinMicLevelFieldTrial << "/Enabled-" << enabled_value << suffix
           << "/";
   return builder.str();
diff --git a/modules/audio_processing/agc2/cpu_features.cc b/modules/audio_processing/agc2/cpu_features.cc
index af50517..80a40cf 100644
--- a/modules/audio_processing/agc2/cpu_features.cc
+++ b/modules/audio_processing/agc2/cpu_features.cc
@@ -19,8 +19,7 @@
 namespace webrtc {
 
 std::string AvailableCpuFeatures::ToString() const {
-  char buf[64];
-  SimpleStringBuilder builder(buf);
+  StringBuilder builder;
   bool first = true;
   if (sse2) {
     builder << (first ? "SSE2" : "_SSE2");
diff --git a/modules/audio_processing/agc2/input_volume_stats_reporter.cc b/modules/audio_processing/agc2/input_volume_stats_reporter.cc
index ed580a8..8190325 100644
--- a/modules/audio_processing/agc2/input_volume_stats_reporter.cc
+++ b/modules/audio_processing/agc2/input_volume_stats_reporter.cc
@@ -51,8 +51,7 @@
 }
 
 metrics::Histogram* CreateVolumeHistogram(InputVolumeType input_volume_type) {
-  char buffer[64];
-  SimpleStringBuilder builder(buffer);
+  StringBuilder builder;
   builder << MetricNamePrefix(input_volume_type) << "OnChange";
   return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(),
                                                   /*min=*/1,
@@ -62,8 +61,7 @@
 
 metrics::Histogram* CreateRateHistogram(InputVolumeType input_volume_type,
                                         absl::string_view name) {
-  char buffer[64];
-  SimpleStringBuilder builder(buffer);
+  StringBuilder builder;
   builder << MetricNamePrefix(input_volume_type) << name;
   return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(),
                                                   /*min=*/1,
@@ -73,8 +71,7 @@
 
 metrics::Histogram* CreateAverageHistogram(InputVolumeType input_volume_type,
                                            absl::string_view name) {
-  char buffer[64];
-  SimpleStringBuilder builder(buffer);
+  StringBuilder builder;
   builder << MetricNamePrefix(input_volume_type) << name;
   return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(),
                                                   /*min=*/1,
diff --git a/modules/audio_processing/logging/apm_data_dumper.cc b/modules/audio_processing/logging/apm_data_dumper.cc
index d86d8d8..2893d2c 100644
--- a/modules/audio_processing/logging/apm_data_dumper.cc
+++ b/modules/audio_processing/logging/apm_data_dumper.cc
@@ -36,8 +36,7 @@
                          int instance_index,
                          int reinit_index,
                          absl::string_view suffix) {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   if (!output_dir.empty()) {
     ss << output_dir;
     if (output_dir.back() != kPathDelimiter) {
@@ -45,7 +44,7 @@
     }
   }
   ss << name << "_" << instance_index << "-" << reinit_index << suffix;
-  return ss.str();
+  return ss.Release();
 }
 #endif
 
diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
index 14dd12e..5cc7cc9 100644
--- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
+++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc
@@ -36,8 +36,7 @@
 class LossBasedBweV2Test : public ::testing::TestWithParam<bool> {
  protected:
   FieldTrials Config(bool enabled, bool valid) {
-    char buffer[1024];
-    SimpleStringBuilder config_string(buffer);
+    StringBuilder config_string;
 
     config_string << "WebRTC-Bwe-LossBasedBweV2/";
 
@@ -77,8 +76,7 @@
   }
 
   FieldTrials ShortObservationConfig(std::string custom_config) {
-    char buffer[1024];
-    SimpleStringBuilder config_string(buffer);
+    StringBuilder config_string;
 
     config_string << "WebRTC-Bwe-LossBasedBweV2/"
                      "MinNumObservations:1,ObservationWindowSize:2,";
diff --git a/modules/desktop_capture/win/desktop_capture_utils.cc b/modules/desktop_capture/win/desktop_capture_utils.cc
index 18c8fa3..710bd22 100644
--- a/modules/desktop_capture/win/desktop_capture_utils.cc
+++ b/modules/desktop_capture/win/desktop_capture_utils.cc
@@ -20,8 +20,7 @@
 
 // Generates a human-readable string from a COM error.
 std::string ComErrorToString(const _com_error& error) {
-  char buffer[1024];
-  webrtc::SimpleStringBuilder string_builder(buffer);
+  webrtc::StringBuilder string_builder;
   // Use _bstr_t to simplify the wchar to char conversion for ErrorMessage().
   _bstr_t error_message(error.ErrorMessage());
   string_builder.AppendFormat("HRESULT: 0x%08X, Message: %s", error.Error(),
diff --git a/modules/video_coding/codecs/av1/av1_svc_config.cc b/modules/video_coding/codecs/av1/av1_svc_config.cc
index fa3b93e..b5f77bf 100644
--- a/modules/video_coding/codecs/av1/av1_svc_config.cc
+++ b/modules/video_coding/codecs/av1/av1_svc_config.cc
@@ -47,14 +47,13 @@
 
 std::optional<ScalabilityMode> BuildScalabilityMode(int num_temporal_layers,
                                                     int num_spatial_layers) {
-  char name[20];
-  SimpleStringBuilder ss(name);
+  StringBuilder ss;
   ss << "L" << num_spatial_layers << "T" << num_temporal_layers;
   if (num_spatial_layers > 1) {
     ss << "_KEY";
   }
 
-  return ScalabilityModeFromString(name);
+  return ScalabilityModeFromString(ss.Release());
 }
 }  // namespace
 
diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc
index 255e8d8..3c84090 100644
--- a/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc
+++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc
@@ -404,8 +404,7 @@
     refresh_view[*settings.update_buffer] = 1;
   }
 
-  char buf[256];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << " spatial_id=" << settings.spatial_id;
   sb << "  ref_idx=[ ";
   for (auto r : ref_frame_config.ref_idx) {
@@ -514,8 +513,7 @@
         settings.rate_options);
   }
 
-  char buf[512];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "GetSvcParams" << " layer bitrates kbps";
   for (int s = 0; s < svc_params.number_spatial_layers; ++s) {
     sb << " S" << s << "=[ ";
diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
index 5c60bdb..b1bce31 100644
--- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
+++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
@@ -131,8 +131,7 @@
 }
 
 std::string CodecSpecificToString(const VideoCodec& codec) {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   switch (codec.codecType) {
     case kVideoCodecVP8:
       ss << "\nnum_temporal_layers: "
@@ -163,7 +162,7 @@
     default:
       break;
   }
-  return ss.str();
+  return ss.Release();
 }
 
 bool RunEncodeInRealTime(const VideoCodecTestFixtureImpl::Config& config) {
@@ -576,8 +575,7 @@
       RTC_LOG(LS_INFO) << layer_stat.ToString("recv_") << "\n";
 
       // For perf dashboard.
-      char modifier_buf[256];
-      SimpleStringBuilder modifier(modifier_buf);
+      StringBuilder modifier;
       modifier << "_r" << rate_profile_idx << "_sl" << layer_stat.spatial_idx;
 
       auto PrintResultHelper = [&modifier, this](
diff --git a/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc b/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc
index 79ff245..7231615 100644
--- a/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc
+++ b/modules/video_coding/codecs/test/videocodec_test_mediacodec.cc
@@ -200,10 +200,9 @@
   static std::string ParamInfoToStr(
       const ::testing::TestParamInfo<
           VideoCodecTestMediaCodecRateAdaptation::ParamType>& info) {
-    char buf[512];
-    webrtc::SimpleStringBuilder ss(buf);
+    StringBuilder ss;
     ss << std::get<0>(info.param).name << "_" << std::get<1>(info.param);
-    return ss.str();
+    return ss.Release();
   }
 };
 
diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc
index d757f1a..bab79b9 100644
--- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc
+++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc
@@ -22,6 +22,7 @@
 #include <numeric>
 #include <optional>
 #include <span>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -131,8 +132,7 @@
     return std::make_unique<ScalableVideoControllerNoLayering>();
   }
 
-  char name[20];
-  SimpleStringBuilder ss(name);
+  StringBuilder ss;
   if (codec.mode == VideoCodecMode::kScreensharing) {
     // TODO(bugs.webrtc.org/11999): Compose names of the structures when they
     // are implemented.
@@ -186,6 +186,7 @@
     }
   }
 
+  std::string name = ss.Release();
   std::optional<ScalabilityMode> scalability_mode =
       ScalabilityModeFromString(name);
   if (!scalability_mode.has_value()) {
diff --git a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc
index 4219ecd..d5bfe54 100644
--- a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc
+++ b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc
@@ -257,14 +257,13 @@
 }  // namespace
 
 std::string Vp9UncompressedHeader::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder oss(buf);
+  StringBuilder oss;
 
   oss << "Vp9UncompressedHeader { " << "profile = " << profile;
 
   if (show_existing_frame) {
     oss << ", show_existing_frame = " << *show_existing_frame << " }";
-    return oss.str();
+    return oss.Release();
   }
 
   oss << ", frame type = " << (is_keyframe ? "key" : "delta")
@@ -383,7 +382,7 @@
   oss << ", compressed_header_size_bytes = " << compressed_header_size;
 
   oss << " }";
-  return oss.str();
+  return oss.Release();
 }
 
 void Parse(BitstreamReader& br,
diff --git a/pc/channel.cc b/pc/channel.cc
index 59921fa..48f46be 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -21,7 +21,6 @@
 #include "absl/algorithm/container.h"
 #include "absl/cleanup/cleanup.h"
 #include "absl/functional/any_invocable.h"
-#include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "api/crypto/crypto_options.h"
 #include "api/jsep.h"
@@ -157,11 +156,9 @@
       // Note: this writes into old_params
       codec.packetization = std::nullopt;
     } else if (!has_matching_packetization) {
-      std::string error_desc = absl::StrFormat(
-          "Failed to set local answer due to incompatible codec "
-          "packetization for pt='%v' specified.",
-          codec.id);
-      return RTCError(RTCErrorType::INTERNAL_ERROR, error_desc);
+      return RTCError(RTCErrorType::INTERNAL_ERROR)
+             << "Failed to set local answer due to incompatible codec "
+             << "packetization for pt='" << codec.id << "' specified.";
     }
 
     if (has_matching_packetization) {
diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc
index cb9f65c..312dc87 100644
--- a/pc/codec_vendor.cc
+++ b/pc/codec_vendor.cc
@@ -585,8 +585,7 @@
       if (codec.type == Codec::Type::kAudio &&
           absl::EqualsIgnoreCase(codec.name, kRedCodecName)) {
         if (codec.params.empty()) {
-          char buffer[100];
-          SimpleStringBuilder param(buffer);
+          StringBuilder param;
           param << codec_payload_type << "/" << codec_payload_type;
           codec.SetParam(kCodecParamNotInNameValueFormat, param.str());
         }
diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc
index 415aa50..1ac63951 100644
--- a/pc/jsep_transport.cc
+++ b/pc/jsep_transport.cc
@@ -323,8 +323,7 @@
   if (*fp_tmp == *fingerprint) {
     return RTCError::OK();
   }
-  char ss_buf[1024];
-  SimpleStringBuilder desc(ss_buf);
+  StringBuilder desc;
   desc << "Local fingerprint does not match identity. Expected: ";
   desc << fp_tmp->ToString();
   desc << " Got: " << fingerprint->ToString();
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 359914e..776ade3 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -99,8 +99,7 @@
     const char direction,
     const std::string& transport_id,
     const RtpCodecParameters& codec_params) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << 'C' << direction << transport_id << '_' << codec_params.payload_type;
   // TODO(https://crbug.com/webrtc/14420): If we stop supporting different FMTP
   // lines for the same PT and transport, which should be illegal SDP, then we
@@ -109,69 +108,62 @@
   if (WriteFmtpParameters(codec_params.parameters, fmtp)) {
     sb << '_' << fmtp.Release();
   }
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCIceCandidatePairStatsIDFromConnectionInfo(
     const ConnectionInfo& info) {
-  char buf[4096];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "CP" << info.local_candidate.id() << "_" << info.remote_candidate.id();
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCTransportStatsIDFromTransportChannel(
     const std::string& transport_name,
     int channel_component) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << 'T' << transport_name << channel_component;
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCInboundRtpStreamStatsIDFromSSRC(const std::string& transport_id,
                                                MediaType media_type,
                                                uint32_t ssrc) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << 'I' << transport_id << (media_type == MediaType::AUDIO ? 'A' : 'V')
      << ssrc;
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCOutboundRtpStreamStatsIDFromSSRC(const std::string& transport_id,
                                                 MediaType media_type,
                                                 uint32_t ssrc) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << 'O' << transport_id << (media_type == MediaType::AUDIO ? 'A' : 'V')
      << ssrc;
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(
     MediaType media_type,
     uint32_t source_ssrc) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "RI" << (media_type == MediaType::AUDIO ? 'A' : 'V') << source_ssrc;
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCRemoteOutboundRTPStreamStatsIDFromSSRC(MediaType media_type,
                                                       uint32_t source_ssrc) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << "RO" << (media_type == MediaType::AUDIO ? 'A' : 'V') << source_ssrc;
-  return sb.str();
+  return sb.Release();
 }
 
 std::string RTCMediaSourceStatsIDFromKindAndAttachment(MediaType media_type,
                                                        int attachment_id) {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << 'S' << (media_type == MediaType::AUDIO ? 'A' : 'V') << attachment_id;
-  return sb.str();
+  return sb.Release();
 }
 
 const char* DataStateToRTCDataChannelState(
diff --git a/rtc_base/logging.cc b/rtc_base/logging.cc
index 90e7a51..59eb6b6 100644
--- a/rtc_base/logging.cc
+++ b/rtc_base/logging.cc
@@ -159,8 +159,7 @@
   }
 
   if (err_ctx != ERRCTX_NONE) {
-    char tmp_buf[1024];
-    SimpleStringBuilder tmp(tmp_buf);
+    StringBuilder tmp;
     tmp.AppendFormat("[0x%08X]", err);
     switch (err_ctx) {
       case ERRCTX_ERRNO:
diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc
index dfbc46a..a1a5a94 100644
--- a/rtc_base/openssl_adapter.cc
+++ b/rtc_base/openssl_adapter.cc
@@ -791,8 +791,7 @@
     default:
       break;
   }
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << SSL_state_string_long(ssl);
   if (ret == 0) {
     RTC_LOG(LS_ERROR) << "Error during " << ss.str() << "\n";
diff --git a/rtc_base/socket_address.cc b/rtc_base/socket_address.cc
index 98455f8..b98a805 100644
--- a/rtc_base/socket_address.cc
+++ b/rtc_base/socket_address.cc
@@ -157,25 +157,22 @@
 }
 
 std::string SocketAddress::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << HostAsURIString() << ":" << port();
-  return sb.str();
+  return sb.Release();
 }
 
 std::string SocketAddress::ToSensitiveString() const {
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << HostAsSensitiveURIString() << ":" << port();
-  return sb.str();
+  return sb.Release();
 }
 
 std::string SocketAddress::ToSensitiveNameAndAddressString() const {
   if (IsUnresolvedIP() || literal_ || hostname_.empty()) {
     return ToSensitiveString();
   }
-  char buf[1024];
-  SimpleStringBuilder sb(buf);
+  StringBuilder sb;
   sb << HostAsSensitiveURIString() << ":" << port();
   sb << " (";
   if (ip_.family() == AF_INET6) {
@@ -185,7 +182,7 @@
   }
   sb << ":" << port() << ")";
 
-  return sb.str();
+  return sb.Release();
 }
 
 bool SocketAddress::FromString(absl::string_view str) {
diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc
index af8e7e8..8d12167 100644
--- a/rtc_base/strings/string_builder.cc
+++ b/rtc_base/strings/string_builder.cc
@@ -50,8 +50,6 @@
 //   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).
-// TODO(tommi): Switch to std::to_chars in C++17.
-
 SimpleStringBuilder& SimpleStringBuilder::operator<<(int i) {
   return AppendFormat("%d", i);
 }
diff --git a/rtc_base/strings/string_builder.h b/rtc_base/strings/string_builder.h
index 4a3df25..a3e0f0a 100644
--- a/rtc_base/strings/string_builder.h
+++ b/rtc_base/strings/string_builder.h
@@ -27,7 +27,7 @@
 // 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 SimpleStringBuilder {
+class [[deprecated("Use webrtc::StringBuilder instead")]] SimpleStringBuilder {
  public:
   explicit SimpleStringBuilder(std::span<char> buffer);
   SimpleStringBuilder(const SimpleStringBuilder&) = delete;
@@ -72,22 +72,13 @@
     return size_ <= buffer_.size() - 1 && buffer_[size_] == '\0';
   }
 
-  // An always-zero-terminated fixed-size buffer that we write to. The fixed
-  // size allows the buffer to be stack allocated, which helps performance.
-  // Having a fixed size is furthermore useful to avoid unnecessary resizing
-  // while building it.
   const std::span<char> buffer_;
-
-  // Represents the number of characters written to the buffer.
-  // This does not include the terminating '\0'.
   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() = default;
@@ -100,7 +91,10 @@
     return *this;
   }
 
-  StringBuilder& operator<<(char c) = delete;
+  StringBuilder& operator<<(char c) {
+    str_ += c;
+    return *this;
+  }
 
   StringBuilder& operator<<(int i) {
     str_ += absl::StrCat(i);
diff --git a/rtc_base/strings/string_builder_unittest.cc b/rtc_base/strings/string_builder_unittest.cc
index 4764ac4..185ff1a 100644
--- a/rtc_base/strings/string_builder_unittest.cc
+++ b/rtc_base/strings/string_builder_unittest.cc
@@ -33,6 +33,9 @@
   std::string value_;
 };
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
 TEST(SimpleStringBuilder, Limit) {
   char sb_buf[10];
   SimpleStringBuilder sb(sb_buf);
@@ -78,8 +81,6 @@
   EXPECT_STREQ(sb.str(), "absl-stringify");
 }
 
-// These tests are safe to run if we have death test support or if DCHECKs are
-// off.
 #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 
 TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharP) {
@@ -129,6 +130,8 @@
 
 #endif
 
+#pragma clang diagnostic pop
+
 ////////////////////////////////////////////////////////////////////////////////
 // StringBuilder.
 
diff --git a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc
index b78e51f..e21fec2 100644
--- a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc
+++ b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc
@@ -69,11 +69,9 @@
   size_t transfer_size;
 
   std::string ToString() {
-    char buffer[64];
-    webrtc::SimpleStringBuilder sb(buffer);
+    webrtc::StringBuilder sb;
     sb << packet_size << "," << transfer_size;
-
-    return sb.str();
+    return sb.Release();
   }
 
   static SetupMessage FromString(absl::string_view sv) {
diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc
index 05e1ac5..8537722 100644
--- a/rtc_tools/frame_analyzer/frame_analyzer.cc
+++ b/rtc_tools/frame_analyzer/frame_analyzer.cc
@@ -163,7 +163,7 @@
     value_stream << "} " << ToString(metric.unit) << " ("
                  << ToString(metric.improvement_direction) << ")";
 
-    fprintf(output_, "RESULT: %s\n", value_stream.str().c_str());
+    fprintf(output_, "RESULT: %s\n", value_stream.Release().c_str());
   }
 
   FILE* const output_;
@@ -231,15 +231,14 @@
   const webrtc::test::ColorTransformationMatrix color_transformation =
       CalculateColorTransformationMatrix(aligned_reference_video, test_video);
 
-  char buf[256];
-  webrtc::SimpleStringBuilder string_builder(buf);
+  webrtc::StringBuilder string_builder;
   for (int i = 0; i < 3; ++i) {
     string_builder << "\n";
     for (int j = 0; j < 4; ++j)
       string_builder.AppendFormat("%6.2f ", color_transformation[i][j]);
   }
   printf("Adjusting test video with color transformation: %s\n",
-         string_builder.str());
+         string_builder.Release().c_str());
 
   // Adjust all frames in the test video with the calculated color
   // transformation.
diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc b/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc
index 559ad78..0dfc5b1 100644
--- a/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc
+++ b/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc
@@ -62,8 +62,7 @@
 std::string GetStreamName(const ParsedRtcEventLog& parsed_log,
                           PacketDirection direction,
                           uint32_t ssrc) {
-  char buffer[200];
-  SimpleStringBuilder name(buffer);
+  StringBuilder name;
   if (IsAudioSsrc(parsed_log, direction, ssrc)) {
     name << "Audio ";
   } else if (IsVideoSsrc(parsed_log, direction, ssrc)) {
@@ -79,15 +78,14 @@
   else
     name << "(Out) ";
   name << "SSRC " << ssrc;
-  return name.str();
+  return name.Release();
 }
 
 std::string GetLayerName(LayerDescription layer) {
-  char buffer[100];
-  SimpleStringBuilder name(buffer);
+  StringBuilder name;
   name << "SSRC " << layer.ssrc << " sl " << layer.spatial_layer << ", tl "
        << layer.temporal_layer;
-  return name.str();
+  return name.Release();
 }
 
 std::string SsrcToString(uint32_t ssrc) {
diff --git a/rtc_tools/unpack_aecdump/unpack.cc b/rtc_tools/unpack_aecdump/unpack.cc
index 08e49e1..584611d 100644
--- a/rtc_tools/unpack_aecdump/unpack.cc
+++ b/rtc_tools/unpack_aecdump/unpack.cc
@@ -224,7 +224,7 @@
     if (file_ == nullptr) {
       StringBuilder file_name;
       file_name << setting_name_ << frame_offset_ << ".txt";
-      file_ = OpenFile(file_name.str(), "wb");
+      file_ = OpenFile(file_name.Release(), "wb");
     }
 
     // Time in the current WAV file, in seconds.
@@ -305,7 +305,7 @@
   } else {
     suffix << frame_count;
   }
-  return suffix.str();
+  return suffix.Release();
 }
 
 }  // namespace
@@ -344,7 +344,7 @@
   StringBuilder callorder_raw_name;
   callorder_raw_name << absl::GetFlag(FLAGS_callorder_file) << ".char";
   FILE* callorder_char_file = WritingCallOrderFile()
-                                  ? OpenFile(callorder_raw_name.str(), "wb")
+                                  ? OpenFile(callorder_raw_name.Release(), "wb")
                                   : nullptr;
   FILE* settings_file = OpenFile(absl::GetFlag(FLAGS_settings_file), "wb");
 
@@ -579,21 +579,21 @@
         StringBuilder reverse_name;
         reverse_name << absl::GetFlag(FLAGS_reverse_file) << suffix << ".wav";
         reverse_wav_file.reset(new WavWriter(
-            reverse_name.str(), reverse_sample_rate, num_reverse_channels));
+            reverse_name.Release(), reverse_sample_rate, num_reverse_channels));
         StringBuilder input_name;
         input_name << absl::GetFlag(FLAGS_input_file) << suffix << ".wav";
-        input_wav_file.reset(new WavWriter(input_name.str(), input_sample_rate,
-                                           num_input_channels));
+        input_wav_file.reset(new WavWriter(
+            input_name.Release(), input_sample_rate, num_input_channels));
         StringBuilder output_name;
         output_name << absl::GetFlag(FLAGS_output_file) << suffix << ".wav";
         output_wav_file.reset(new WavWriter(
-            output_name.str(), output_sample_rate, num_output_channels));
+            output_name.Release(), output_sample_rate, num_output_channels));
 
         if (WritingCallOrderFile()) {
           StringBuilder callorder_name;
           callorder_name << absl::GetFlag(FLAGS_callorder_file) << suffix
                          << ".char";
-          callorder_char_file = OpenFile(callorder_name.str(), "wb");
+          callorder_char_file = OpenFile(callorder_name.Release(), "wb");
         }
 
         if (WritingRuntimeSettingFiles()) {
diff --git a/rtc_tools/video_encoder/encoded_image_file_writer.cc b/rtc_tools/video_encoder/encoded_image_file_writer.cc
index 68ca11b..b88b66e 100644
--- a/rtc_tools/video_encoder/encoded_image_file_writer.cc
+++ b/rtc_tools/video_encoder/encoded_image_file_writer.cc
@@ -11,6 +11,7 @@
 
 #include <cstddef>
 #include <optional>
+#include <string>
 #include <utility>
 
 #include "api/video/encoded_image.h"
@@ -45,14 +46,14 @@
   // Create writer for every decode target.
   for (int i = 0; i < spatial_layers_; ++i) {
     for (int j = 0; j < temporal_layers_; ++j) {
-      char buffer[256];
-      SimpleStringBuilder name(buffer);
+      StringBuilder name;
       name << "output-" << codec_string << "-"
            << ScalabilityModeToString(*scalability_mode) << "-L" << i << "T"
            << j << ".ivf";
-
-      decode_target_writers_.emplace_back(std::make_pair(
-          IvfFileWriter::Wrap(name.str(), /*byte_limit=*/0), name.str()));
+      std::string name_str = name.Release();
+      auto writer = IvfFileWriter::Wrap(name_str, /*byte_limit=*/0);
+      decode_target_writers_.emplace_back(std::move(writer),
+                                          std::move(name_str));
     }
   }
 }
diff --git a/rtc_tools/video_encoder/video_encoder.cc b/rtc_tools/video_encoder/video_encoder.cc
index bab90e5..fbcb84f 100644
--- a/rtc_tools/video_encoder/video_encoder.cc
+++ b/rtc_tools/video_encoder/video_encoder.cc
@@ -111,8 +111,7 @@
 }
 
 std::string ToString(const EncodedImage& encoded_image) {
-  char buffer[1024];
-  SimpleStringBuilder ss(buffer);
+  StringBuilder ss;
 
   ss << VideoFrameTypeToString(encoded_image.frame_type())
      << ", size=" << encoded_image.size() << ", qp=" << encoded_image.qp_
@@ -130,13 +129,12 @@
     ss << ", TemporalIndex=" << *encoded_image.TemporalIndex();
   }
 
-  return ss.str();
+  return ss.Release();
 }
 
 [[maybe_unused]] std::string ToString(
     const CodecSpecificInfo& codec_specific_info) {
-  char buffer[1024];
-  SimpleStringBuilder ss(buffer);
+  StringBuilder ss;
 
   ss << CodecTypeToPayloadString(codec_specific_info.codecType);
 
@@ -160,7 +158,7 @@
        << ", num_chains=" << template_structure->num_chains;
   }
 
-  return ss.str();
+  return ss.Release();
 }
 
 // This follows
diff --git a/sdk/android/src/jni/audio_device/aaudio_wrapper.cc b/sdk/android/src/jni/audio_device/aaudio_wrapper.cc
index 6c42380..48cc231 100644
--- a/sdk/android/src/jni/audio_device/aaudio_wrapper.cc
+++ b/sdk/android/src/jni/audio_device/aaudio_wrapper.cc
@@ -411,8 +411,7 @@
 
 void AAudioWrapper::LogStreamConfiguration() {
   RTC_DCHECK(stream_);
-  char ss_buf[1024];
-  webrtc::SimpleStringBuilder ss(ss_buf);
+  StringBuilder ss;
   ss << "Stream Configuration: ";
   ss << "sample rate=" << sample_rate() << ", channels=" << channel_count();
   ss << ", samples per frame=" << samples_per_frame();
diff --git a/test/network/cross_traffic.cc b/test/network/cross_traffic.cc
index 0a3deaf..fb1affc 100644
--- a/test/network/cross_traffic.cc
+++ b/test/network/cross_traffic.cc
@@ -79,7 +79,7 @@
 ColumnPrinter RandomWalkCrossTraffic::StatsPrinter() {
   return ColumnPrinter::Lambda(
       "random_walk_cross_traffic_rate",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         sb.AppendFormat("%.0lf", TrafficRate().bps() / 8.0);
       },
       32);
@@ -130,7 +130,7 @@
 ColumnPrinter PulsedPeaksCrossTraffic::StatsPrinter() {
   return ColumnPrinter::Lambda(
       "pulsed_peaks_cross_traffic_rate",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         sb.AppendFormat("%.0lf", TrafficRate().bps() / 8.0);
       },
       32);
diff --git a/test/network/emulated_turn_server.cc b/test/network/emulated_turn_server.cc
index ff2fc96b..d3706be 100644
--- a/test/network/emulated_turn_server.cc
+++ b/test/network/emulated_turn_server.cc
@@ -164,11 +164,9 @@
     turn_server_->AddInternalSocket(std::move(client_socket), PROTO_UDP);
     turn_server_->SetExternalSocketFactory(new PacketSocketFactoryWrapper(this),
                                            SocketAddress());
-    char buf[256];
-    SimpleStringBuilder str(buf);
-    str.AppendFormat("turn:%s?transport=udp",
-                     client_address_.ToString().c_str());
-    ice_config_.url = str.str();
+    StringBuilder sb;
+    sb << "turn:" << client_address_.ToString() << "?transport=udp";
+    ice_config_.url = sb.Release();
   });
 }
 
diff --git a/test/network/network_emulation_manager.cc b/test/network/network_emulation_manager.cc
index 02b36d6..af6a197 100644
--- a/test/network/network_emulation_manager.cc
+++ b/test/network/network_emulation_manager.cc
@@ -384,12 +384,11 @@
     EmulatedTURNServerConfig config) {
   auto* client = CreateEndpoint(config.client_config);
   auto* peer = CreateEndpoint(config.client_config);
-  char buf[128];
-  SimpleStringBuilder str(buf);
-  str.AppendFormat("turn_server_%u",
-                   static_cast<unsigned>(turn_servers_.size()));
+  StringBuilder thread_name;
+  thread_name << "turn_server_" << turn_servers_.size();
   auto turn = std::make_unique<EmulatedTURNServer>(
-      env_, config, time_controller_->CreateThread(str.str()), client, peer);
+      env_, config, time_controller_->CreateThread(thread_name.Release()),
+      client, peer);
   auto out = turn.get();
   turn_servers_.push_back(std::move(turn));
   return out;
diff --git a/test/scenario/audio_stream.cc b/test/scenario/audio_stream.cc
index 78d6b06..f18683b 100644
--- a/test/scenario/audio_stream.cc
+++ b/test/scenario/audio_stream.cc
@@ -185,7 +185,7 @@
 ColumnPrinter SendAudioStream::StatsPrinter() {
   return ColumnPrinter::Lambda(
       "audio_target_rate",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         sender_->SendTask([this, &sb] {
           AudioSendStream::Stats stats = send_stream_->GetStats();
           sb.AppendFormat("%.0lf", stats.target_bitrate_bps / 8.0);
diff --git a/test/scenario/call_client.cc b/test/scenario/call_client.cc
index d6c538c..4f30076 100644
--- a/test/scenario/call_client.cc
+++ b/test/scenario/call_client.cc
@@ -271,7 +271,7 @@
 ColumnPrinter CallClient::StatsPrinter() {
   return ColumnPrinter::Lambda(
       "pacer_delay call_send_bw",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         Call::Stats call_stats = call_->GetStats();
         sb.AppendFormat("%.3lf %.0lf", call_stats.pacer_delay_ms / 1000.0,
                         call_stats.send_bandwidth_bps / 8.0);
diff --git a/test/scenario/column_printer.cc b/test/scenario/column_printer.cc
index ecc7d36..d6c48e7 100644
--- a/test/scenario/column_printer.cc
+++ b/test/scenario/column_printer.cc
@@ -27,20 +27,18 @@
 ColumnPrinter::~ColumnPrinter() = default;
 
 ColumnPrinter::ColumnPrinter(const char* headers,
-                             std::function<void(SimpleStringBuilder&)> printer,
+                             std::function<void(StringBuilder&)> printer,
                              size_t max_length)
     : headers_(headers), printer_(printer), max_length_(max_length) {}
 
 ColumnPrinter ColumnPrinter::Fixed(const char* headers, std::string fields) {
   return ColumnPrinter(
-      headers, [fields](SimpleStringBuilder& sb) { sb << fields; },
-      fields.size());
+      headers, [fields](StringBuilder& sb) { sb << fields; }, fields.size());
 }
 
-ColumnPrinter ColumnPrinter::Lambda(
-    const char* headers,
-    std::function<void(SimpleStringBuilder&)> printer,
-    size_t max_length) {
+ColumnPrinter ColumnPrinter::Lambda(const char* headers,
+                                    std::function<void(StringBuilder&)> printer,
+                                    size_t max_length) {
   return ColumnPrinter(headers, printer, max_length);
 }
 
@@ -69,7 +67,7 @@
 void StatesPrinter::PrintRow() {
   // Note that this is run for null output to preserve side effects, this allows
   // setting break points etc.
-  SimpleStringBuilder sb(buffer_);
+  StringBuilder sb;
   printers_[0].printer_(sb);
   for (size_t i = 1; i < printers_.size(); ++i) {
     sb << ' ';
@@ -77,7 +75,7 @@
   }
   sb << "\n";
   if (writer_)
-    writer_->Write(std::string(sb.str(), sb.size()));
+    writer_->Write(sb.str());
 }
 }  // namespace test
 }  // namespace webrtc
diff --git a/test/scenario/column_printer.h b/test/scenario/column_printer.h
index 28b75c8..c5a9342 100644
--- a/test/scenario/column_printer.h
+++ b/test/scenario/column_printer.h
@@ -27,18 +27,18 @@
   static ColumnPrinter Fixed(const char* headers, std::string fields);
   static ColumnPrinter Lambda(
       const char* headers,
-      std::function<void(webrtc::SimpleStringBuilder&)> printer,
+      std::function<void(webrtc::StringBuilder&)> printer,
       size_t max_length = 256);
 
  protected:
   friend class StatesPrinter;
   const char* headers_;
-  std::function<void(webrtc::SimpleStringBuilder&)> printer_;
+  std::function<void(webrtc::StringBuilder&)> printer_;
   size_t max_length_;
 
  private:
   ColumnPrinter(const char* headers,
-                std::function<void(webrtc::SimpleStringBuilder&)> printer,
+                std::function<void(webrtc::StringBuilder&)> printer,
                 size_t max_length);
 };
 
diff --git a/test/scenario/network_node.cc b/test/scenario/network_node.cc
index 8a4b802..e6bb430 100644
--- a/test/scenario/network_node.cc
+++ b/test/scenario/network_node.cc
@@ -87,7 +87,7 @@
 
 ColumnPrinter SimulationNode::ConfigPrinter() const {
   return ColumnPrinter::Lambda(
-      "propagation_delay capacity loss_rate", [this](SimpleStringBuilder& sb) {
+      "propagation_delay capacity loss_rate", [this](StringBuilder& sb) {
         sb.AppendFormat("%.3lf %.0lf %.2lf", config_.delay.seconds<double>(),
                         config_.bandwidth.bps() / 8.0, config_.loss_rate);
       });
diff --git a/test/scenario/scenario.cc b/test/scenario/scenario.cc
index a858afa..f947c2a 100644
--- a/test/scenario/scenario.cc
+++ b/test/scenario/scenario.cc
@@ -112,7 +112,7 @@
 ColumnPrinter Scenario::TimePrinter() {
   return ColumnPrinter::Lambda(
       "time",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         sb.AppendFormat("%.3lf", Now().seconds<double>());
       },
       32);
diff --git a/test/scenario/video_stream.cc b/test/scenario/video_stream.cc
index 86e5111..5c2927a 100644
--- a/test/scenario/video_stream.cc
+++ b/test/scenario/video_stream.cc
@@ -525,7 +525,7 @@
 ColumnPrinter SendVideoStream::StatsPrinter() {
   return ColumnPrinter::Lambda(
       "video_target_rate video_sent_rate width height",
-      [this](SimpleStringBuilder& sb) {
+      [this](StringBuilder& sb) {
         VideoSendStream::Stats video_stats = send_stream_->GetStats();
         int width = 0;
         int height = 0;
diff --git a/video/config/video_encoder_config.cc b/video/config/video_encoder_config.cc
index d6f2e0c..8eef969 100644
--- a/video/config/video_encoder_config.cc
+++ b/video/config/video_encoder_config.cc
@@ -36,8 +36,7 @@
 VideoStream::~VideoStream() = default;
 
 std::string VideoStream::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{width: " << width;
   ss << ", height: " << height;
   ss << ", max_framerate: " << max_framerate;
@@ -54,7 +53,7 @@
        << scale_resolution_down_to->height;
   }
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 VideoEncoderConfig::VideoEncoderConfig()
@@ -76,8 +75,7 @@
 VideoEncoderConfig::~VideoEncoderConfig() = default;
 
 std::string VideoEncoderConfig::ToString() const {
-  char buf[1024];
-  SimpleStringBuilder ss(buf);
+  StringBuilder ss;
   ss << "{codec_type: " << CodecTypeToPayloadString(codec_type);
   ss << ", content_type: ";
   switch (content_type) {
@@ -100,7 +98,7 @@
     ss << ", simulcast_layers[" << n << "]: " << simulcast_layers[n].ToString();
   }
   ss << '}';
-  return ss.str();
+  return ss.Release();
 }
 
 bool VideoEncoderConfig::HasScaleResolutionDownTo() const {
diff --git a/video/frame_dumping_encoder.cc b/video/frame_dumping_encoder.cc
index 3377989..fa98174 100644
--- a/video/frame_dumping_encoder.cc
+++ b/video/frame_dumping_encoder.cc
@@ -108,11 +108,10 @@
  private:
   std::string FilenameFromSimulcastIndex(int index)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
-    char filename_buffer[1024];
-    SimpleStringBuilder builder(filename_buffer);
+    StringBuilder builder;
     builder << output_directory_ << "/webrtc_encoded_frames" << "."
             << origin_time_micros_ << "." << index << ".ivf";
-    return builder.str();
+    return builder.Release();
   }
 
   IvfFileWriter& GetFileWriterForSimulcastIndex(int index)
diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc
index ca4b7df..ba8313a 100644
--- a/video/receive_statistics_proxy.cc
+++ b/video/receive_statistics_proxy.cc
@@ -121,8 +121,7 @@
     const StreamDataCounters* rtx_stats) {
   RTC_DCHECK_RUN_ON(&main_thread_);
 
-  char log_stream_buf[8 * 1024];
-  SimpleStringBuilder log_stream(log_stream_buf);
+  StringBuilder log_stream;
 
   Timestamp now = clock_->CurrentTime();
   TimeDelta stream_duration = now - start_;
diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc
index abb746d..92b247b 100644
--- a/video/send_statistics_proxy.cc
+++ b/video/send_statistics_proxy.cc
@@ -303,8 +303,7 @@
     RTC_CHECK_LT(*spatial_id, kMaxSpatialLayers);
   }
 
-  char buffer[100];
-  webrtc::SimpleStringBuilder ssb(buffer);
+  StringBuilder ssb;
   ssb << uma_prefix_ << "Psnr";
   if (spatial_id.has_value()) {
     ssb << ".S" << *spatial_id;
@@ -386,8 +385,7 @@
   RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
   const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
   const int kMinRequiredPeriodicSamples = 6;
-  char log_stream_buf[8 * 1024];
-  SimpleStringBuilder log_stream(log_stream_buf);
+  StringBuilder log_stream;
   int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
   int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
   if (in_width != -1) {
diff --git a/video/video_quality_observer2.cc b/video/video_quality_observer2.cc
index 90354d4..08385bd2 100644
--- a/video/video_quality_observer2.cc
+++ b/video/video_quality_observer2.cc
@@ -66,8 +66,7 @@
     return;
   }
 
-  char log_stream_buf[2 * 1024];
-  SimpleStringBuilder log_stream(log_stream_buf);
+  StringBuilder log_stream;
 
   if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
     smooth_playback_durations_.Add(last_frame_rendered_ms_ -
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index b219817..c4ff0bd 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -371,8 +371,7 @@
 
   std::vector<std::string> encoded_frame_dump_files;
   if (!params_.logging.encoded_frame_base_path.empty()) {
-    char ss_buf[100];
-    SimpleStringBuilder sb(ss_buf);
+    StringBuilder sb;
     sb << send_logs_++;
     std::string prefix =
         params_.logging.encoded_frame_base_path + "." + sb.str() + ".send.";
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 4faf80c..87ddade 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -1288,8 +1288,7 @@
         encoder_config_, &codec);
   }
 
-  char log_stream_buf[4 * 1024];
-  SimpleStringBuilder log_stream(log_stream_buf);
+  StringBuilder log_stream;
   log_stream << "ReconfigureEncoder: simulcast streams: ";
   for (size_t i = 0; i < codec.numberOfSimulcastStreams; ++i) {
     log_stream << "{" << i << ": " << codec.simulcastStream[i].width << "x"