|  | // 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 "quiche/common/quiche_buffer_allocator.h" | 
|  |  | 
|  | #include <cstring> | 
|  |  | 
|  | #include "quiche/common/platform/api/quiche_bug_tracker.h" | 
|  | #include "quiche/common/platform/api/quiche_logging.h" | 
|  | #include "quiche/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 |