Internal change

PiperOrigin-RevId: 432953313
diff --git a/common/quiche_buffer_allocator.cc b/common/quiche_buffer_allocator.cc
new file mode 100644
index 0000000..0801173
--- /dev/null
+++ b/common/quiche_buffer_allocator.cc
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "common/quiche_buffer_allocator.h"
+
+#include <cstring>
+
+#include "common/platform/api/quiche_bug_tracker.h"
+#include "common/platform/api/quiche_logging.h"
+#include "common/platform/api/quiche_prefetch.h"
+
+namespace quiche {
+
+QuicheBuffer QuicheBuffer::CopyFromIovec(QuicheBufferAllocator* allocator,
+                                         const struct iovec* iov, int iov_count,
+                                         size_t iov_offset,
+                                         size_t buffer_length) {
+  if (buffer_length == 0) {
+    return {};
+  }
+
+  int iovnum = 0;
+  while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) {
+    iov_offset -= iov[iovnum].iov_len;
+    ++iovnum;
+  }
+  QUICHE_DCHECK_LE(iovnum, iov_count);
+  if (iovnum >= iov_count) {
+    QUICHE_BUG(quiche_bug_10839_1)
+        << "iov_offset larger than iovec total size.";
+    return {};
+  }
+  QUICHE_DCHECK_LE(iov_offset, iov[iovnum].iov_len);
+
+  // Unroll the first iteration that handles iov_offset.
+  const size_t iov_available = iov[iovnum].iov_len - iov_offset;
+  size_t copy_len = std::min(buffer_length, iov_available);
+
+  // Try to prefetch the next iov if there is at least one more after the
+  // current. Otherwise, it looks like an irregular access that the hardware
+  // prefetcher won't speculatively prefetch. Only prefetch one iov because
+  // generally, the iov_offset is not 0, input iov consists of 2K buffers and
+  // the output buffer is ~1.4K.
+  if (copy_len == iov_available && iovnum + 1 < iov_count) {
+    char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base);
+    // Prefetch 2 cachelines worth of data to get the prefetcher started; leave
+    // it to the hardware prefetcher after that.
+    quiche::QuichePrefetchT0(next_base);
+    if (iov[iovnum + 1].iov_len >= 64) {
+      quiche::QuichePrefetchT0(next_base + ABSL_CACHELINE_SIZE);
+    }
+  }
+
+  QuicheBuffer buffer(allocator, buffer_length);
+
+  const char* src = static_cast<char*>(iov[iovnum].iov_base) + iov_offset;
+  char* dst = buffer.data();
+  while (true) {
+    memcpy(dst, src, copy_len);
+    buffer_length -= copy_len;
+    dst += copy_len;
+    if (buffer_length == 0 || ++iovnum >= iov_count) {
+      break;
+    }
+    src = static_cast<char*>(iov[iovnum].iov_base);
+    copy_len = std::min(buffer_length, iov[iovnum].iov_len);
+  }
+
+  QUICHE_BUG_IF(quiche_bug_10839_2, buffer_length > 0)
+      << "iov_offset + buffer_length larger than iovec total size.";
+
+  return buffer;
+}
+
+}  // namespace quiche
diff --git a/common/quiche_buffer_allocator.h b/common/quiche_buffer_allocator.h
index 40140ac..b0c233b 100644
--- a/common/quiche_buffer_allocator.h
+++ b/common/quiche_buffer_allocator.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "absl/strings/string_view.h"
+#include "quic/platform/api/quic_iovec.h"
 #include "common/platform/api/quiche_export.h"
 
 namespace quiche {
@@ -88,11 +89,19 @@
   // Factory method to create a QuicheBuffer that holds a copy of `data`.
   static QuicheBuffer Copy(QuicheBufferAllocator* allocator,
                            absl::string_view data) {
-    QuicheBuffer result(allocator, data.size());
-    memcpy(result.data(), data.data(), data.size());
-    return result;
+    QuicheBuffer buffer(allocator, data.size());
+    memcpy(buffer.data(), data.data(), data.size());
+    return buffer;
   }
 
+  // Factory method to create a QuicheBuffer of length `buffer_length` that
+  // holds a copy of `buffer_length` bytes from `iov` starting at offset
+  // `iov_offset`.  `iov` must be at least `iov_offset + buffer_length` total
+  // length.
+  static QuicheBuffer CopyFromIovec(QuicheBufferAllocator* allocator,
+                                    const struct iovec* iov, int iov_count,
+                                    size_t iov_offset, size_t buffer_length);
+
   const char* data() const { return buffer_.get(); }
   char* data() { return buffer_.get(); }
   size_t size() const { return size_; }
diff --git a/common/quiche_buffer_allocator_test.cc b/common/quiche_buffer_allocator_test.cc
new file mode 100644
index 0000000..7d08cf1
--- /dev/null
+++ b/common/quiche_buffer_allocator_test.cc
@@ -0,0 +1,141 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "common/quiche_buffer_allocator.h"
+
+#include "absl/strings/string_view.h"
+#include "common/platform/api/quiche_test.h"
+#include "common/platform/api/quiche_test_helpers.h"
+#include "common/simple_buffer_allocator.h"
+#include "common/test_tools/quiche_test_utils.h"
+
+namespace quiche {
+namespace test {
+namespace {
+
+TEST(QuicheBuffer, CopyFromEmpty) {
+  SimpleBufferAllocator allocator;
+  QuicheBuffer buffer = QuicheBuffer::Copy(&allocator, "");
+  EXPECT_TRUE(buffer.empty());
+}
+
+TEST(QuicheBuffer, Copy) {
+  SimpleBufferAllocator allocator;
+  QuicheBuffer buffer = QuicheBuffer::Copy(&allocator, "foobar");
+  EXPECT_EQ("foobar", buffer.AsStringView());
+}
+
+TEST(QuicheBuffer, CopyFromIovecZeroBytes) {
+  const int buffer_length = 0;
+
+  SimpleBufferAllocator allocator;
+  QuicheBuffer buffer = QuicheBuffer::CopyFromIovec(
+      &allocator, nullptr,
+      /* iov_count = */ 0, /* iov_offset = */ 0, buffer_length);
+  EXPECT_TRUE(buffer.empty());
+
+  constexpr absl::string_view kData("foobar");
+  iovec iov = MakeIOVector(kData);
+
+  buffer = QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                       /* iov_count = */ 1,
+                                       /* iov_offset = */ 0, buffer_length);
+  EXPECT_TRUE(buffer.empty());
+
+  buffer = QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                       /* iov_count = */ 1,
+                                       /* iov_offset = */ 3, buffer_length);
+  EXPECT_TRUE(buffer.empty());
+}
+
+TEST(QuicheBuffer, CopyFromIovecSimple) {
+  constexpr absl::string_view kData("foobar");
+  iovec iov = MakeIOVector(kData);
+
+  SimpleBufferAllocator allocator;
+  QuicheBuffer buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                  /* iov_count = */ 1, /* iov_offset = */ 0,
+                                  /* buffer_length = */ 6);
+  EXPECT_EQ("foobar", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                  /* iov_count = */ 1, /* iov_offset = */ 0,
+                                  /* buffer_length = */ 3);
+  EXPECT_EQ("foo", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                  /* iov_count = */ 1, /* iov_offset = */ 3,
+                                  /* buffer_length = */ 3);
+  EXPECT_EQ("bar", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov,
+                                  /* iov_count = */ 1, /* iov_offset = */ 1,
+                                  /* buffer_length = */ 4);
+  EXPECT_EQ("ooba", buffer.AsStringView());
+}
+
+TEST(QuicheBuffer, CopyFromIovecMultiple) {
+  constexpr absl::string_view kData1("foo");
+  constexpr absl::string_view kData2("bar");
+  iovec iov[] = {MakeIOVector(kData1), MakeIOVector(kData2)};
+
+  SimpleBufferAllocator allocator;
+  QuicheBuffer buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 0,
+                                  /* buffer_length = */ 6);
+  EXPECT_EQ("foobar", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 0,
+                                  /* buffer_length = */ 3);
+  EXPECT_EQ("foo", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 3,
+                                  /* buffer_length = */ 3);
+  EXPECT_EQ("bar", buffer.AsStringView());
+
+  buffer =
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 1,
+                                  /* buffer_length = */ 4);
+  EXPECT_EQ("ooba", buffer.AsStringView());
+}
+
+TEST(QuicheBuffer, CopyFromIovecOffsetTooLarge) {
+  constexpr absl::string_view kData1("foo");
+  constexpr absl::string_view kData2("bar");
+  iovec iov[] = {MakeIOVector(kData1), MakeIOVector(kData2)};
+
+  SimpleBufferAllocator allocator;
+  EXPECT_QUICHE_BUG(
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 10,
+                                  /* buffer_length = */ 6),
+      "iov_offset larger than iovec total size");
+}
+
+TEST(QuicheBuffer, CopyFromIovecTooManyBytesRequested) {
+  constexpr absl::string_view kData1("foo");
+  constexpr absl::string_view kData2("bar");
+  iovec iov[] = {MakeIOVector(kData1), MakeIOVector(kData2)};
+
+  SimpleBufferAllocator allocator;
+  EXPECT_QUICHE_BUG(
+      QuicheBuffer::CopyFromIovec(&allocator, &iov[0],
+                                  /* iov_count = */ 2, /* iov_offset = */ 2,
+                                  /* buffer_length = */ 10),
+      "iov_offset [+] buffer_length larger than iovec total size");
+}
+
+}  // anonymous namespace
+}  // namespace test
+}  // namespace quiche
diff --git a/common/quiche_mem_slice_storage.cc b/common/quiche_mem_slice_storage.cc
index 2b1b5e1..d57cf76 100644
--- a/common/quiche_mem_slice_storage.cc
+++ b/common/quiche_mem_slice_storage.cc
@@ -23,9 +23,8 @@
   size_t io_offset = 0;
   while (write_len > 0) {
     size_t slice_len = std::min(write_len, max_slice_len);
-    QuicheBuffer buffer(allocator, slice_len);
-    quic::QuicUtils::CopyToBuffer(iov, iov_count, io_offset, slice_len,
-                                  buffer.data());
+    QuicheBuffer buffer = QuicheBuffer::CopyFromIovec(allocator, iov, iov_count,
+                                                      io_offset, slice_len);
     storage_.push_back(QuicheMemSlice(std::move(buffer)));
     write_len -= slice_len;
     io_offset += slice_len;
diff --git a/common/test_tools/quiche_test_utils.cc b/common/test_tools/quiche_test_utils.cc
index 9a3bb21..5ebe527 100644
--- a/common/test_tools/quiche_test_utils.cc
+++ b/common/test_tools/quiche_test_utils.cc
@@ -87,5 +87,9 @@
                 << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
 }
 
+iovec MakeIOVector(absl::string_view str) {
+  return iovec{const_cast<char*>(str.data()), static_cast<size_t>(str.size())};
+}
+
 }  // namespace test
 }  // namespace quiche
diff --git a/common/test_tools/quiche_test_utils.h b/common/test_tools/quiche_test_utils.h
index 45a526b..becfea0 100644
--- a/common/test_tools/quiche_test_utils.h
+++ b/common/test_tools/quiche_test_utils.h
@@ -7,6 +7,9 @@
 
 #include <string>
 
+#include "absl/strings/string_view.h"
+#include "quic/platform/api/quic_iovec.h"
+
 namespace quiche {
 namespace test {
 
@@ -16,6 +19,9 @@
                                    const char* expected,
                                    const int expected_len);
 
+// Create iovec that points to that data that `str` points to.
+iovec MakeIOVector(absl::string_view str);
+
 }  // namespace test
 }  // namespace quiche
 
diff --git a/quic/core/quic_stream_send_buffer.h b/quic/core/quic_stream_send_buffer.h
index cf3fa13..e972ac0 100644
--- a/quic/core/quic_stream_send_buffer.h
+++ b/quic/core/quic_stream_send_buffer.h
@@ -10,7 +10,6 @@
 #include "quic/core/quic_interval_deque.h"
 #include "quic/core/quic_interval_set.h"
 #include "quic/core/quic_types.h"
-#include "quic/platform/api/quic_iovec.h"
 #include "common/platform/api/quiche_mem_slice.h"
 #include "common/quiche_circular_deque.h"
 
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index aa8f59e..94d35eb 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -24,7 +24,6 @@
 #include "quic/platform/api/quic_flags.h"
 #include "common/platform/api/quiche_logging.h"
 #include "common/platform/api/quiche_mem_slice.h"
-#include "common/platform/api/quiche_prefetch.h"
 #include "common/quiche_endian.h"
 
 namespace quic {
@@ -239,57 +238,6 @@
 }
 
 // static
-void QuicUtils::CopyToBuffer(const struct iovec* iov,
-                             int iov_count,
-                             size_t iov_offset,
-                             size_t buffer_length,
-                             char* buffer) {
-  int iovnum = 0;
-  while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) {
-    iov_offset -= iov[iovnum].iov_len;
-    ++iovnum;
-  }
-  QUICHE_DCHECK_LE(iovnum, iov_count);
-  QUICHE_DCHECK_LE(iov_offset, iov[iovnum].iov_len);
-  if (iovnum >= iov_count || buffer_length == 0) {
-    return;
-  }
-
-  // Unroll the first iteration that handles iov_offset.
-  const size_t iov_available = iov[iovnum].iov_len - iov_offset;
-  size_t copy_len = std::min(buffer_length, iov_available);
-
-  // Try to prefetch the next iov if there is at least one more after the
-  // current. Otherwise, it looks like an irregular access that the hardware
-  // prefetcher won't speculatively prefetch. Only prefetch one iov because
-  // generally, the iov_offset is not 0, input iov consists of 2K buffers and
-  // the output buffer is ~1.4K.
-  if (copy_len == iov_available && iovnum + 1 < iov_count) {
-    char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base);
-    // Prefetch 2 cachelines worth of data to get the prefetcher started; leave
-    // it to the hardware prefetcher after that.
-    quiche::QuichePrefetchT0(next_base);
-    if (iov[iovnum + 1].iov_len >= 64) {
-      quiche::QuichePrefetchT0(next_base + ABSL_CACHELINE_SIZE);
-    }
-  }
-
-  const char* src = static_cast<char*>(iov[iovnum].iov_base) + iov_offset;
-  while (true) {
-    memcpy(buffer, src, copy_len);
-    buffer_length -= copy_len;
-    buffer += copy_len;
-    if (buffer_length == 0 || ++iovnum >= iov_count) {
-      break;
-    }
-    src = static_cast<char*>(iov[iovnum].iov_base);
-    copy_len = std::min(buffer_length, iov[iovnum].iov_len);
-  }
-  QUIC_BUG_IF(quic_bug_10839_1, buffer_length > 0)
-      << "Failed to copy entire length to buffer.";
-}
-
-// static
 bool QuicUtils::IsAckable(SentPacketState state) {
   return state != NEVER_SENT && state != ACKED && state != UNACKABLE;
 }
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 085f532..ea03236 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -21,7 +21,6 @@
 #include "quic/core/quic_types.h"
 #include "quic/core/quic_versions.h"
 #include "quic/platform/api/quic_export.h"
-#include "quic/platform/api/quic_iovec.h"
 #include "quic/platform/api/quic_socket_address.h"
 #include "common/platform/api/quiche_mem_slice.h"
 
@@ -72,15 +71,6 @@
       const QuicSocketAddress& old_address,
       const QuicSocketAddress& new_address);
 
-  // Copies |buffer_length| bytes from iov starting at offset |iov_offset| into
-  // buffer. |iov| must be at least iov_offset+length total length and buffer
-  // must be at least |length| long.
-  static void CopyToBuffer(const struct iovec* iov,
-                           int iov_count,
-                           size_t iov_offset,
-                           size_t buffer_length,
-                           char* buffer);
-
   // Returns the opposite Perspective of the |perspective| passed in.
   static constexpr Perspective InvertPerspective(Perspective perspective) {
     return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 869e416..d9bc541 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -1510,12 +1510,6 @@
   return l;
 }
 
-// Utility function that stores |str|'s data in |iov|.
-inline void MakeIOVector(absl::string_view str, struct iovec* iov) {
-  iov->iov_base = const_cast<char*>(str.data());
-  iov->iov_len = static_cast<size_t>(str.size());
-}
-
 // Helper functions for stream ids, to allow test logic to abstract over the
 // HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used
 // per HTTP transaction).