Use `absl::StringResizeAndOverwrite()` instead of `std::string::resize()`
when the string is filled soon.

Rename `ResizeStringAmortized()` to `StringResizeAmortized()` for consistency,
and add `StringResizeAndOverwriteAmortized()`.

Use `absl::AppendCordToString()`, which is now available, instead of a custom
`riegeli::cord_internal::AppendCordToString()`.

Cosmetics: for `std::string`, replace `&dest[i]` with `dest.data() + i`
in remaining places. This relies on C++17.
PiperOrigin-RevId: 858971838
diff --git a/riegeli/base/BUILD b/riegeli/base/BUILD
index 0f89f66..ae37b53 100644
--- a/riegeli/base/BUILD
+++ b/riegeli/base/BUILD
@@ -146,7 +146,9 @@
     hdrs = ["string_utils.h"],
     deps = [
         ":arithmetic",
+        ":assert",
         "@com_google_absl//absl/base:nullability",
+        "@com_google_absl//absl/strings:resize_and_overwrite",
     ],
 )
 
@@ -181,6 +183,7 @@
             ":arithmetic",
             "@com_google_absl//absl/base:core_headers",
             "@com_google_absl//absl/base:nullability",
+            "@com_google_absl//absl/strings:resize_and_overwrite",
             "@com_google_absl//absl/strings:string_view",
             "@com_google_absl//absl/types:span",
         ],
@@ -399,6 +402,7 @@
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/base:nullability",
         "@com_google_absl//absl/strings:cord",
+        "@com_google_absl//absl/strings:resize_and_overwrite",
         "@com_google_absl//absl/strings:string_view",
     ],
 )
@@ -560,6 +564,7 @@
         "@com_google_absl//absl/container:inlined_vector",
         "@com_google_absl//absl/meta:type_traits",
         "@com_google_absl//absl/strings:cord",
+        "@com_google_absl//absl/strings:resize_and_overwrite",
         "@com_google_absl//absl/strings:string_view",
         "@com_google_absl//absl/types:span",
     ],
diff --git a/riegeli/base/chain.cc b/riegeli/base/chain.cc
index 4dfaac1..009e606 100644
--- a/riegeli/base/chain.cc
+++ b/riegeli/base/chain.cc
@@ -28,6 +28,7 @@
 #include "absl/base/optimization.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/cord.h"
+#include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 #include "riegeli/base/arithmetic.h"
@@ -522,8 +523,10 @@
 inline std::string Chain::ToString() const {
   if (begin_ == end_) return std::string(short_data());
   std::string dest;
-  dest.resize(size_);
-  CopyToSlow(&dest[0]);
+  absl::StringResizeAndOverwrite(dest, size_, [&](char* data, size_t size) {
+    CopyToSlow(data);
+    return size;
+  });
   return dest;
 }
 
@@ -593,12 +596,15 @@
 }
 
 void Chain::AppendTo(std::string& dest) const& {
-  const size_t size_before = dest.size();
-  RIEGELI_CHECK_LE(size_, std::numeric_limits<size_t>::max() - size_before)
+  const size_t old_size = dest.size();
+  RIEGELI_CHECK_LE(size_, std::numeric_limits<size_t>::max() - old_size)
       << "Failed precondition of Chain::AppendTo(string&): "
          "string size overflow";
-  ResizeStringAmortized(dest, size_before + size_);
-  CopyTo(&dest[size_before]);
+  riegeli::StringResizeAndOverwriteAmortized(dest, old_size + size_,
+                                             [&](char* data, size_t size) {
+                                               CopyTo(data + old_size);
+                                               return size;
+                                             });
 }
 
 void Chain::AppendTo(std::string& dest) && {
@@ -616,12 +622,15 @@
       }
     }
   }
-  const size_t size_before = dest.size();
-  RIEGELI_CHECK_LE(size_, std::numeric_limits<size_t>::max() - size_before)
+  const size_t old_size = dest.size();
+  RIEGELI_CHECK_LE(size_, std::numeric_limits<size_t>::max() - old_size)
       << "Failed precondition of Chain::AppendTo(string&): "
          "string size overflow";
-  ResizeStringAmortized(dest, size_before + size_);
-  CopyTo(&dest[size_before]);
+  riegeli::StringResizeAndOverwriteAmortized(dest, old_size + size_,
+                                             [&](char* data, size_t size) {
+                                               CopyTo(data + old_size);
+                                               return size;
+                                             });
 }
 
 void Chain::AppendTo(absl::Cord& dest) const& {
diff --git a/riegeli/base/cord_iterator_span.cc b/riegeli/base/cord_iterator_span.cc
index 585ccc8..1107f3b 100644
--- a/riegeli/base/cord_iterator_span.cc
+++ b/riegeli/base/cord_iterator_span.cc
@@ -22,6 +22,7 @@
 #include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/strings/cord.h"
+#include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/string_view.h"
 #include "riegeli/base/assert.h"
 #include "riegeli/base/string_utils.h"
@@ -57,8 +58,11 @@
     return chunk.substr(0, length);
   }
   scratch.clear();
-  ResizeStringAmortized(scratch, length);
-  ReadSlow(iter, length, scratch.data());
+  riegeli::StringResizeAndOverwriteAmortized(scratch, length,
+                                             [&](char* data, size_t size) {
+                                               ReadSlow(iter, size, data);
+                                               return size;
+                                             });
   return scratch;
 }
 
@@ -66,8 +70,10 @@
   absl::Cord::CharIterator& iter = *iterator_;
   size_t length = length_;
   dest.clear();
-  dest.resize(length);
-  Read(iter, length, dest.data());
+  absl::StringResizeAndOverwrite(dest, length, [&](char* data, size_t size) {
+    Read(iter, size, data);
+    return size;
+  });
 }
 
 }  // namespace riegeli
diff --git a/riegeli/base/cord_utils.cc b/riegeli/base/cord_utils.cc
index 9e4f492..239f104 100644
--- a/riegeli/base/cord_utils.cc
+++ b/riegeli/base/cord_utils.cc
@@ -17,7 +17,6 @@
 #include <stddef.h>
 
 #include <cstring>
-#include <string>
 #include <utility>
 
 #include "absl/base/nullability.h"
@@ -39,12 +38,6 @@
   }
 }
 
-void AppendCordToString(const absl::Cord& src, std::string& dest) {
-  const size_t old_size = dest.size();
-  ResizeStringAmortized(dest, old_size + src.size());
-  CopyCordToArray(src, &dest[old_size]);
-}
-
 absl::Cord MakeBlockyCord(absl::string_view src) {
   absl::Cord dest;
   AppendToBlockyCord(src, dest);
diff --git a/riegeli/base/cord_utils.h b/riegeli/base/cord_utils.h
index 38ea2e2..c3611d2 100644
--- a/riegeli/base/cord_utils.h
+++ b/riegeli/base/cord_utils.h
@@ -18,8 +18,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <string>
-
 #include "absl/base/nullability.h"
 #include "absl/numeric/bits.h"
 #include "absl/strings/cord.h"
@@ -75,9 +73,6 @@
 // `nullptr` only if `src.empty()`.
 void CopyCordToArray(const absl::Cord& src, char* absl_nullable dest);
 
-// Appends `src` to `dest`.
-void AppendCordToString(const absl::Cord& src, std::string& dest);
-
 // Variants of `absl::Cord` operations with different block sizing tradeoffs:
 //  * `MakeBlockyCord(src)` is like `absl::Cord(src)`.
 //  * `AssignToBlockyCord(src, dest)` is like `dest = src`.
diff --git a/riegeli/base/string_utils.cc b/riegeli/base/string_utils.cc
index cf42af2..ea77059 100644
--- a/riegeli/base/string_utils.cc
+++ b/riegeli/base/string_utils.cc
@@ -23,18 +23,14 @@
 
 ABSL_POINTERS_DEFAULT_NONNULL
 
-namespace riegeli {
+namespace riegeli::string_utils_internal {
 
-void ResizeStringAmortized(std::string& dest, size_t new_size) {
-  if (new_size > dest.capacity()) {
-    dest.reserve(
-        dest.capacity() == std::string().capacity()
-            ? new_size
-            : UnsignedMax(new_size,
-                          UnsignedMin(dest.capacity() + dest.capacity() / 2,
-                                      dest.max_size())));
-  }
-  dest.resize(new_size);
+void ReserveAmortized(std::string& dest, size_t new_size) {
+  dest.reserve(dest.capacity() == std::string().capacity()
+                   ? new_size
+                   : UnsignedMax(new_size, UnsignedMin(dest.capacity() +
+                                                           dest.capacity() / 2,
+                                                       dest.max_size())));
 }
 
-}  // namespace riegeli
+}  // namespace riegeli::string_utils_internal
diff --git a/riegeli/base/string_utils.h b/riegeli/base/string_utils.h
index d8a799a..25c6d99 100644
--- a/riegeli/base/string_utils.h
+++ b/riegeli/base/string_utils.h
@@ -18,16 +18,43 @@
 #include <stddef.h>
 
 #include <string>
+#include <utility>
 
 #include "absl/base/nullability.h"
+#include "absl/strings/resize_and_overwrite.h"
+#include "riegeli/base/assert.h"
 
 ABSL_POINTERS_DEFAULT_NONNULL
 
 namespace riegeli {
 
-// Resizes `dest` to `new_size`, ensuring that repeated growth has the cost
-// proportional to the final size. New contents are unspecified.
-void ResizeStringAmortized(std::string& dest, size_t new_size);
+namespace string_utils_internal {
+
+void ReserveAmortized(std::string& dest, size_t new_size);
+
+}  // namespace string_utils_internal
+
+// Like `std::string::resize()`, ensuring that repeated growth has the cost
+// proportional to the final size.
+inline void StringResizeAmortized(std::string& dest, size_t new_size) {
+  if (new_size > dest.capacity()) {
+    string_utils_internal::ReserveAmortized(dest, new_size);
+  }
+  RIEGELI_ASSUME_GE(dest.capacity(), new_size);
+  dest.resize(new_size);
+}
+
+// Like `absl::StringResizeAndOverwrite()`, ensuring that repeated growth has
+// the cost proportional to the final size.
+template <typename Op>
+inline void StringResizeAndOverwriteAmortized(std::string& dest,
+                                              size_t new_size, Op op) {
+  if (new_size > dest.capacity()) {
+    string_utils_internal::ReserveAmortized(dest, new_size);
+  }
+  RIEGELI_ASSUME_GE(dest.capacity(), new_size);
+  return absl::StringResizeAndOverwrite(dest, new_size, std::move(op));
+}
 
 }  // namespace riegeli
 
diff --git a/riegeli/base/unicode.cc b/riegeli/base/unicode.cc
index 877dd4c..3cfcbbe 100644
--- a/riegeli/base/unicode.cc
+++ b/riegeli/base/unicode.cc
@@ -28,6 +28,7 @@
 
 #include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
+#include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 #include "riegeli/base/arithmetic.h"
@@ -49,7 +50,7 @@
   if (ABSL_PREDICT_FALSE(dest_size == 0)) return false;
   dest.resize(IntCast<size_t>(dest_size));
   MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src.data(),
-                      IntCast<int>(src.size()), &dest[0], dest_size);
+                      IntCast<int>(src.size()), dest.data(), dest_size);
   return true;
 }
 
@@ -64,10 +65,13 @@
       CP_UTF8, WC_ERR_INVALID_CHARS, src.data(), IntCast<int>(src.size()),
       nullptr, 0, nullptr, nullptr);
   if (ABSL_PREDICT_FALSE(dest_size == 0)) return false;
-  dest.resize(IntCast<size_t>(dest_size));
-  WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src.data(),
-                      IntCast<int>(src.size()), &dest[0], dest_size, nullptr,
-                      nullptr);
+  absl::StringResizeAndOverwrite(
+      dest, IntCast<size_t>(dest_size), [&](char* data, size_t size) {
+        WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src.data(),
+                            IntCast<int>(src.size()), data, IntCast<int>(size),
+                            nullptr, nullptr);
+        return size;
+      });
   return true;
 }
 
@@ -77,10 +81,13 @@
   const int dest_size = WideCharToMultiByte(CP_UTF8, 0, src.data(),
                                             SaturatingIntCast<int>(src.size()),
                                             nullptr, 0, nullptr, nullptr);
-  dest.resize(IntCast<size_t>(dest_size));
-  WideCharToMultiByte(CP_UTF8, 0, src.data(),
-                      SaturatingIntCast<int>(src.size()), &dest[0], dest_size,
-                      nullptr, nullptr);
+  absl::StringResizeAndOverwrite(
+      dest, IntCast<size_t>(dest_size), [&](char* data, size_t size) {
+        WideCharToMultiByte(CP_UTF8, 0, src.data(),
+                            SaturatingIntCast<int>(src.size()), data,
+                            IntCast<int>(size), nullptr, nullptr);
+        return size;
+      });
   return dest;
 }
 
diff --git a/riegeli/bytes/BUILD b/riegeli/bytes/BUILD
index 1b14a10..5152c86 100644
--- a/riegeli/bytes/BUILD
+++ b/riegeli/bytes/BUILD
@@ -146,6 +146,7 @@
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:cord",
+        "@com_google_absl//absl/strings:resize_and_overwrite",
         "@com_google_absl//absl/strings:string_view",
     ],
 )
diff --git a/riegeli/bytes/read_all.cc b/riegeli/bytes/read_all.cc
index d61e574..6357d04 100644
--- a/riegeli/bytes/read_all.cc
+++ b/riegeli/bytes/read_all.cc
@@ -25,6 +25,7 @@
 #include "absl/base/optimization.h"
 #include "absl/status/status.h"
 #include "absl/strings/cord.h"
+#include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "riegeli/base/arithmetic.h"
@@ -100,16 +101,20 @@
       return absl::OkStatus();
     }
     size_t remaining_max_length = max_length;
-    const size_t dest_pos = dest.size();
-    if (src.available() < dest.capacity() - dest_pos) {
+    const size_t old_size = dest.size();
+    if (src.available() < dest.capacity() - old_size) {
       // Try to fill all remaining space in `dest`, to avoid copying through the
       // `Chain` in case the remaining length is smaller.
       const size_t length =
-          UnsignedMin(dest.capacity() - dest_pos, remaining_max_length);
-      dest.resize(dest_pos + length);
+          UnsignedMin(dest.capacity() - old_size, remaining_max_length);
+      bool read_ok;
       size_t length_read;
-      if (!src.Read(length, &dest[dest_pos], &length_read)) {
-        dest.erase(dest_pos + length_read);
+      absl::StringResizeAndOverwrite(
+          dest, old_size + length, [&](char* data, size_t size) {
+            read_ok = src.Read(size - old_size, data + old_size, &length_read);
+            return old_size + length_read;
+          });
+      if (!read_ok) {
         if (ABSL_PREDICT_FALSE(!src.ok())) return src.status();
         return absl::OkStatus();
       }
diff --git a/riegeli/bytes/reader.cc b/riegeli/bytes/reader.cc
index d231095..1e0bcd7 100644
--- a/riegeli/bytes/reader.cc
+++ b/riegeli/bytes/reader.cc
@@ -385,14 +385,15 @@
   RIEGELI_ASSERT_LE(length, std::numeric_limits<size_t>::max() - dest.size())
       << "Failed precondition of Reader::ReadSlow(string&): "
          "string size overflow";
-  const size_t dest_pos = dest.size();
-  ResizeStringAmortized(dest, dest_pos + length);
-  size_t length_read;
-  if (ABSL_PREDICT_FALSE(!ReadSlow(length, &dest[dest_pos], length_read))) {
-    dest.erase(dest_pos + length_read);
-    return false;
-  }
-  return true;
+  const size_t old_size = dest.size();
+  bool read_ok;
+  riegeli::StringResizeAndOverwriteAmortized(
+      dest, old_size + length, [&](char* data, size_t size) {
+        size_t length_read;
+        read_ok = ReadSlow(size - old_size, data + old_size, length_read);
+        return old_size + length_read;
+      });
+  return read_ok;
 }
 
 bool Reader::ReadSlow(size_t length, std::string& dest, size_t& length_read) {
@@ -623,11 +624,14 @@
                     std::numeric_limits<size_t>::max() - dest.size())
       << "Failed precondition of Reader::ReadSomeSlow(string&): "
          "string size overflow";
-  const size_t dest_pos = dest.size();
-  ResizeStringAmortized(dest, dest_pos + max_length);
-  size_t length_read;
-  const bool read_ok = ReadSomeSlow(max_length, &dest[dest_pos], length_read);
-  dest.erase(dest_pos + length_read);
+  const size_t old_size = dest.size();
+  bool read_ok;
+  riegeli::StringResizeAndOverwriteAmortized(
+      dest, old_size + max_length, [&](char* data, size_t size) {
+        size_t length_read;
+        read_ok = ReadSomeSlow(size - old_size, data + old_size, length_read);
+        return old_size + length_read;
+      });
   return read_ok;
 }
 
diff --git a/riegeli/bytes/resizable_writer.h b/riegeli/bytes/resizable_writer.h
index 3afd257..2e70004 100644
--- a/riegeli/bytes/resizable_writer.h
+++ b/riegeli/bytes/resizable_writer.h
@@ -327,7 +327,7 @@
 template <typename Alloc = std::allocator<char>>
 struct StringResizableTraits {
   using Resizable = std::basic_string<char, std::char_traits<char>, Alloc>;
-  static char* Data(Resizable& dest) { return &dest[0]; }
+  static char* Data(Resizable& dest) { return dest.data(); }
   static size_t Size(const Resizable& dest) { return dest.size(); }
   static constexpr bool kIsStable = false;
   static bool Resize(Resizable& dest, size_t new_size, size_t used_size) {
diff --git a/riegeli/bytes/string_writer.cc b/riegeli/bytes/string_writer.cc
index d08e9f0..af4ece4 100644
--- a/riegeli/bytes/string_writer.cc
+++ b/riegeli/bytes/string_writer.cc
@@ -65,7 +65,7 @@
   RIEGELI_ASSERT(!uses_secondary_buffer())
       << "Failed precondition in StringWriterBase::MakeDestBuffer(): "
          "secondary buffer is used";
-  set_buffer(&dest[0], dest.size(), cursor_index);
+  set_buffer(dest.data(), dest.size(), cursor_index);
   set_start_pos(0);
 }
 
@@ -170,7 +170,7 @@
     const size_t new_cursor_index = cursor_index + src.size();
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        std::memcpy(&dest[cursor_index], src.data(), src.size());
+        std::memcpy(dest.data() + cursor_index, src.data(), src.size());
       } else {
         dest.erase(cursor_index);
         // TODO: When `absl::string_view` becomes C++17
@@ -210,7 +210,7 @@
     const size_t new_cursor_index = cursor_index + src.size();
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        src.CopyTo(&dest[cursor_index]);
+        src.CopyTo(dest.data() + cursor_index);
       } else {
         dest.erase(cursor_index);
         src.AppendTo(dest);
@@ -248,7 +248,7 @@
     const size_t new_cursor_index = cursor_index + src.size();
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        src.CopyTo(&dest[cursor_index]);
+        src.CopyTo(dest.data() + cursor_index);
       } else {
         dest.erase(cursor_index);
         std::move(src).AppendTo(dest);
@@ -286,10 +286,10 @@
     const size_t new_cursor_index = cursor_index + src.size();
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        cord_internal::CopyCordToArray(src, &dest[cursor_index]);
+        cord_internal::CopyCordToArray(src, dest.data() + cursor_index);
       } else {
         dest.erase(cursor_index);
-        cord_internal::AppendCordToString(src, dest);
+        absl::AppendCordToString(src, &dest);
       }
       GrowDestToCapacityAndMakeBuffer(dest, new_cursor_index);
       return true;
@@ -324,10 +324,10 @@
     const size_t new_cursor_index = cursor_index + src.size();
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        cord_internal::CopyCordToArray(src, &dest[cursor_index]);
+        cord_internal::CopyCordToArray(src, dest.data() + cursor_index);
       } else {
         dest.erase(cursor_index);
-        cord_internal::AppendCordToString(src, dest);
+        absl::AppendCordToString(src, &dest);
       }
       GrowDestToCapacityAndMakeBuffer(dest, new_cursor_index);
       return true;
@@ -362,7 +362,7 @@
     const size_t new_cursor_index = cursor_index + IntCast<size_t>(src.size());
     if (new_cursor_index <= dest.capacity()) {
       if (ABSL_PREDICT_FALSE(new_cursor_index <= dest.size())) {
-        std::memset(&dest[cursor_index], src.fill(),
+        std::memset(dest.data() + cursor_index, src.fill(),
                     IntCast<size_t>(src.size()));
       } else {
         dest.erase(cursor_index);
diff --git a/riegeli/bytes/string_writer.h b/riegeli/bytes/string_writer.h
index 0f371d0..334c9f3 100644
--- a/riegeli/bytes/string_writer.h
+++ b/riegeli/bytes/string_writer.h
@@ -357,7 +357,7 @@
   void Done(StringWriter& self) {
     if (uses_buffer_) {
       std::string& dest = *self.dest_;
-      self.set_buffer(&dest[0], dest.size(), start_to_cursor_);
+      self.set_buffer(dest.data(), dest.size(), start_to_cursor_);
     }
   }
 
diff --git a/riegeli/messages/serialize_message.cc b/riegeli/messages/serialize_message.cc
index ba7a21e..942586d 100644
--- a/riegeli/messages/serialize_message.cc
+++ b/riegeli/messages/serialize_message.cc
@@ -85,9 +85,9 @@
       << "Failed to serialize message of type " << src.GetTypeName()
       << ": SerializeWithCachedSizes() failed for an unknown reason";
   RIEGELI_ASSERT_EQ(IntCast<size_t>(coded_stream.ByteCount()), size)
-      << "Byte size calculation and serialization were inconsistent. This "
-         "may indicate a bug in protocol buffers or it may be caused by "
-         "concurrent modification of "
+      << "Byte size calculation and serialization were inconsistent. "
+         "This may indicate a bug in protocol buffers "
+         "or it may be caused by concurrent modification of "
       << src.GetTypeName();
   return absl::OkStatus();
 }
@@ -107,9 +107,9 @@
         << src.GetTypeName()
         << " was modified concurrently during serialization";
     RIEGELI_ASSERT_EQ(PtrDistance(dest.cursor(), cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
+        << "Byte size calculation and serialization were inconsistent. "
+           "This may indicate a bug in protocol buffers "
+           "or it may be caused by concurrent modification of "
         << src.GetTypeName();
     dest.set_cursor(cursor);
     return absl::OkStatus();
@@ -133,9 +133,9 @@
         << src.GetTypeName()
         << " was modified concurrently during serialization";
     RIEGELI_ASSERT_EQ(PtrDistance(dest.cursor(), cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
+        << "Byte size calculation and serialization were inconsistent. "
+           "This may indicate a bug in protocol buffers "
+           "or it may be caused by concurrent modification of "
         << src.GetTypeName();
     return absl::OkStatus();
   }
@@ -222,30 +222,35 @@
     return FailSizeOverflow(src, size);
   }
   dest.clear();
-  ResizeStringAmortized(dest, size);
-  if (options.deterministic() == std::nullopt) {
-    // Creating a string, which is necessarily flat.
-    // `SerializeWithCachedSizesToArray()` is faster than
-    // `SerializeWithCachedSizes()`.
-    char* const cursor =
-        reinterpret_cast<char*>(src.SerializeWithCachedSizesToArray(
-            reinterpret_cast<uint8_t*>(&dest[0])));
-    RIEGELI_ASSERT_EQ(src.ByteSizeLong(), size)
-        << src.GetTypeName()
-        << " was modified concurrently during serialization";
-    RIEGELI_ASSERT_EQ(PtrDistance(dest.data(), cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
-        << src.GetTypeName();
-    return absl::OkStatus();
-  }
-  riegeli::ArrayWriter writer(&dest[0], size);
-  const absl::Status status =
-      SerializeMessageUsingStream(src, writer, options.deterministic(), size);
-  RIEGELI_EVAL_ASSERT(writer.Close()) << "ArrayWriter has no reason to fail "
-                                         "if the size does not overflow: "
-                                      << writer.status();
+  absl::Status status;
+  riegeli::StringResizeAndOverwriteAmortized(
+      dest, size, [&](char* data, size_t size) {
+        if (options.deterministic() == std::nullopt) {
+          // Creating a string, which is necessarily flat.
+          // `SerializeWithCachedSizesToArray()` is faster than
+          // `SerializeWithCachedSizes()`.
+          char* const cursor =
+              reinterpret_cast<char*>(src.SerializeWithCachedSizesToArray(
+                  reinterpret_cast<uint8_t*>(data)));
+          RIEGELI_ASSERT_EQ(src.ByteSizeLong(), size)
+              << src.GetTypeName()
+              << " was modified concurrently during serialization";
+          RIEGELI_ASSERT_EQ(PtrDistance(data, cursor), size)
+              << "Byte size calculation and serialization were inconsistent. "
+                 "This may indicate a bug in protocol buffers "
+                 "or it may be caused by concurrent modification of "
+              << src.GetTypeName();
+          return size;
+        }
+        riegeli::ArrayWriter writer(data, size);
+        status = SerializeMessageUsingStream(src, writer,
+                                             options.deterministic(), size);
+        RIEGELI_EVAL_ASSERT(writer.Close())
+            << "ArrayWriter has no reason to fail "
+               "if the size does not overflow: "
+            << writer.status();
+        return writer.written().size();
+      });
   return status;
 }
 
@@ -272,9 +277,9 @@
         << src.GetTypeName()
         << " was modified concurrently during serialization";
     RIEGELI_ASSERT_EQ(PtrDistance(data, cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
+        << "Byte size calculation and serialization were inconsistent. "
+           "This may indicate a bug in protocol buffers "
+           "or it may be caused by concurrent modification of "
         << src.GetTypeName();
     return absl::OkStatus();
   }
@@ -312,9 +317,9 @@
         << src.GetTypeName()
         << " was modified concurrently during serialization";
     RIEGELI_ASSERT_EQ(PtrDistance(buffer.data(), cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
+        << "Byte size calculation and serialization were inconsistent. "
+           "This may indicate a bug in protocol buffers "
+           "or it may be caused by concurrent modification of "
         << src.GetTypeName();
     return absl::OkStatus();
   }
@@ -358,9 +363,9 @@
         << src.GetTypeName()
         << " was modified concurrently during serialization";
     RIEGELI_ASSERT_EQ(PtrDistance(buffer.data(), cursor), size)
-        << "Byte size calculation and serialization were inconsistent. This "
-           "may indicate a bug in protocol buffers or it may be caused by "
-           "concurrent modification of "
+        << "Byte size calculation and serialization were inconsistent. "
+           "This may indicate a bug in protocol buffers "
+           "or it may be caused by concurrent modification of "
         << src.GetTypeName();
     dest.Append(std::move(buffer));
     return absl::OkStatus();
diff --git a/riegeli/records/record_position.cc b/riegeli/records/record_position.cc
index 8fc06ec..3d62b1b 100644
--- a/riegeli/records/record_position.cc
+++ b/riegeli/records/record_position.cc
@@ -81,9 +81,9 @@
 bool RecordPosition::FromBytes(absl::string_view serialized) {
   if (serialized.size() == 2 * sizeof(uint64_t)) {
     // Reading the old format is temporarily supported too.
-    const uint64_t chunk_begin = ReadBigEndian<uint64_t>(&serialized[0]);
+    const uint64_t chunk_begin = ReadBigEndian<uint64_t>(serialized.data());
     const uint64_t record_index =
-        ReadBigEndian<uint64_t>(&serialized[sizeof(uint64_t)]);
+        ReadBigEndian<uint64_t>(serialized.data() + sizeof(uint64_t));
     if (ABSL_PREDICT_FALSE(record_index > std::numeric_limits<uint64_t>::max() -
                                               chunk_begin)) {
       return false;