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