xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/quiche_buffer_allocator.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/common/quiche_buffer_allocator.h"
6 
7 #include <cstring>
8 
9 #include "quiche/common/platform/api/quiche_bug_tracker.h"
10 #include "quiche/common/platform/api/quiche_logging.h"
11 #include "quiche/common/platform/api/quiche_prefetch.h"
12 
13 namespace quiche {
14 
CopyFromIovec(QuicheBufferAllocator * allocator,const struct iovec * iov,int iov_count,size_t iov_offset,size_t buffer_length)15 QuicheBuffer QuicheBuffer::CopyFromIovec(QuicheBufferAllocator* allocator,
16                                          const struct iovec* iov, int iov_count,
17                                          size_t iov_offset,
18                                          size_t buffer_length) {
19   if (buffer_length == 0) {
20     return {};
21   }
22 
23   int iovnum = 0;
24   while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) {
25     iov_offset -= iov[iovnum].iov_len;
26     ++iovnum;
27   }
28   QUICHE_DCHECK_LE(iovnum, iov_count);
29   if (iovnum >= iov_count) {
30     QUICHE_BUG(quiche_bug_10839_1)
31         << "iov_offset larger than iovec total size.";
32     return {};
33   }
34   QUICHE_DCHECK_LE(iov_offset, iov[iovnum].iov_len);
35 
36   // Unroll the first iteration that handles iov_offset.
37   const size_t iov_available = iov[iovnum].iov_len - iov_offset;
38   size_t copy_len = std::min(buffer_length, iov_available);
39 
40   // Try to prefetch the next iov if there is at least one more after the
41   // current. Otherwise, it looks like an irregular access that the hardware
42   // prefetcher won't speculatively prefetch. Only prefetch one iov because
43   // generally, the iov_offset is not 0, input iov consists of 2K buffers and
44   // the output buffer is ~1.4K.
45   if (copy_len == iov_available && iovnum + 1 < iov_count) {
46     char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base);
47     // Prefetch 2 cachelines worth of data to get the prefetcher started; leave
48     // it to the hardware prefetcher after that.
49     quiche::QuichePrefetchT0(next_base);
50     if (iov[iovnum + 1].iov_len >= 64) {
51       quiche::QuichePrefetchT0(next_base + ABSL_CACHELINE_SIZE);
52     }
53   }
54 
55   QuicheBuffer buffer(allocator, buffer_length);
56 
57   const char* src = static_cast<char*>(iov[iovnum].iov_base) + iov_offset;
58   char* dst = buffer.data();
59   while (true) {
60     memcpy(dst, src, copy_len);
61     buffer_length -= copy_len;
62     dst += copy_len;
63     if (buffer_length == 0 || ++iovnum >= iov_count) {
64       break;
65     }
66     src = static_cast<char*>(iov[iovnum].iov_base);
67     copy_len = std::min(buffer_length, iov[iovnum].iov_len);
68   }
69 
70   QUICHE_BUG_IF(quiche_bug_10839_2, buffer_length > 0)
71       << "iov_offset + buffer_length larger than iovec total size.";
72 
73   return buffer;
74 }
75 
76 }  // namespace quiche
77