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