1 // Copyright (c) 2019 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h"
6 
7 #include <sstream>
8 
9 namespace quic {
10 
QuicBatchWriterBuffer()11 QuicBatchWriterBuffer::QuicBatchWriterBuffer() {
12   memset(buffer_, 0, sizeof(buffer_));
13 }
14 
Clear()15 void QuicBatchWriterBuffer::Clear() {
16   buffered_writes_.clear();
17 }
18 
DebugString() const19 std::string QuicBatchWriterBuffer::DebugString() const {
20   std::ostringstream os;
21   os << "{ buffer: " << static_cast<const void*>(buffer_)
22      << " buffer_end: " << static_cast<const void*>(buffer_end())
23      << " buffered_writes_.size(): " << buffered_writes_.size()
24      << " next_write_loc: " << static_cast<const void*>(GetNextWriteLocation())
25      << " SizeInUse: " << SizeInUse() << " }";
26   return os.str();
27 }
28 
Invariants() const29 bool QuicBatchWriterBuffer::Invariants() const {
30   // Buffers in buffered_writes_ should not overlap, and collectively they
31   // should cover a continuous prefix of buffer_.
32   const char* next_buffer = buffer_;
33   for (auto iter = buffered_writes_.begin(); iter != buffered_writes_.end();
34        ++iter) {
35     if ((iter->buffer != next_buffer) ||
36         (iter->buffer + iter->buf_len > buffer_end())) {
37       return false;
38     }
39     next_buffer += iter->buf_len;
40   }
41 
42   return static_cast<size_t>(next_buffer - buffer_) == SizeInUse();
43 }
44 
GetNextWriteLocation() const45 char* QuicBatchWriterBuffer::GetNextWriteLocation() const {
46   const char* next_loc =
47       buffered_writes_.empty()
48           ? buffer_
49           : buffered_writes_.back().buffer + buffered_writes_.back().buf_len;
50   if (static_cast<size_t>(buffer_end() - next_loc) < kMaxOutgoingPacketSize) {
51     return nullptr;
52   }
53   return const_cast<char*>(next_loc);
54 }
55 
PushBufferedWrite(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,const PerPacketOptions * options,uint64_t release_time)56 QuicBatchWriterBuffer::PushResult QuicBatchWriterBuffer::PushBufferedWrite(
57     const char* buffer,
58     size_t buf_len,
59     const QuicIpAddress& self_address,
60     const QuicSocketAddress& peer_address,
61     const PerPacketOptions* options,
62     uint64_t release_time) {
63   DCHECK(Invariants());
64   DCHECK_LE(buf_len, kMaxOutgoingPacketSize);
65 
66   PushResult result = {/*succeeded=*/false, /*buffer_copied=*/false};
67   char* next_write_location = GetNextWriteLocation();
68   if (next_write_location == nullptr) {
69     return result;
70   }
71 
72   if (buffer != next_write_location) {
73     if (IsExternalBuffer(buffer, buf_len)) {
74       memcpy(next_write_location, buffer, buf_len);
75     } else if (IsInternalBuffer(buffer, buf_len)) {
76       memmove(next_write_location, buffer, buf_len);
77     } else {
78       QUIC_BUG << "Buffer[" << static_cast<const void*>(buffer) << ", "
79                << static_cast<const void*>(buffer + buf_len)
80                << ") overlaps with internal buffer["
81                << static_cast<const void*>(buffer_) << ", "
82                << static_cast<const void*>(buffer_end()) << ")";
83       return result;
84     }
85     result.buffer_copied = true;
86   } else {
87     // In place push, do nothing.
88   }
89   buffered_writes_.emplace_back(
90       next_write_location, buf_len, self_address, peer_address,
91       options ? options->Clone() : std::unique_ptr<PerPacketOptions>(),
92       release_time);
93 
94   DCHECK(Invariants());
95 
96   result.succeeded = true;
97   return result;
98 }
99 
UndoLastPush()100 void QuicBatchWriterBuffer::UndoLastPush() {
101   if (!buffered_writes_.empty()) {
102     buffered_writes_.pop_back();
103   }
104 }
105 
PopBufferedWrite(int32_t num_buffered_writes)106 QuicBatchWriterBuffer::PopResult QuicBatchWriterBuffer::PopBufferedWrite(
107     int32_t num_buffered_writes) {
108   DCHECK(Invariants());
109   DCHECK_GE(num_buffered_writes, 0);
110   DCHECK_LE(static_cast<size_t>(num_buffered_writes), buffered_writes_.size());
111 
112   PopResult result = {/*num_buffers_popped=*/0,
113                       /*moved_remaining_buffers=*/false};
114 
115   result.num_buffers_popped = std::max<int32_t>(num_buffered_writes, 0);
116   result.num_buffers_popped =
117       std::min<int32_t>(result.num_buffers_popped, buffered_writes_.size());
118   buffered_writes_.pop_front_n(result.num_buffers_popped);
119 
120   if (!buffered_writes_.empty()) {
121     // If not all buffered writes are erased, the remaining ones will not cover
122     // a continuous prefix of buffer_. We'll fix it by moving the remaining
123     // buffers to the beginning of buffer_ and adjust the buffer pointers in all
124     // remaining buffered writes.
125     // This should happen very rarely, about once per write block.
126     result.moved_remaining_buffers = true;
127     const char* buffer_before_move = buffered_writes_.front().buffer;
128     size_t buffer_len_to_move = buffered_writes_.back().buffer +
129                                 buffered_writes_.back().buf_len -
130                                 buffer_before_move;
131     memmove(buffer_, buffer_before_move, buffer_len_to_move);
132 
133     size_t distance_to_move = buffer_before_move - buffer_;
134     for (BufferedWrite& buffered_write : buffered_writes_) {
135       buffered_write.buffer -= distance_to_move;
136     }
137 
138     DCHECK_EQ(buffer_, buffered_writes_.front().buffer);
139   }
140   DCHECK(Invariants());
141 
142   return result;
143 }
144 
SizeInUse() const145 size_t QuicBatchWriterBuffer::SizeInUse() const {
146   if (buffered_writes_.empty()) {
147     return 0;
148   }
149 
150   return buffered_writes_.back().buffer + buffered_writes_.back().buf_len -
151          buffer_;
152 }
153 
154 }  // namespace quic
155