| // Copyright (c) 2019 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 "quic/core/batch_writer/quic_batch_writer_buffer.h" |
| |
| #include <sstream> |
| |
| namespace quic { |
| |
| QuicBatchWriterBuffer::QuicBatchWriterBuffer() { |
| memset(buffer_, 0, sizeof(buffer_)); |
| } |
| |
| void QuicBatchWriterBuffer::Clear() { |
| buffered_writes_.clear(); |
| } |
| |
| std::string QuicBatchWriterBuffer::DebugString() const { |
| std::ostringstream os; |
| os << "{ buffer: " << static_cast<const void*>(buffer_) |
| << " buffer_end: " << static_cast<const void*>(buffer_end()) |
| << " buffered_writes_.size(): " << buffered_writes_.size() |
| << " next_write_loc: " << static_cast<const void*>(GetNextWriteLocation()) |
| << " SizeInUse: " << SizeInUse() << " }"; |
| return os.str(); |
| } |
| |
| bool QuicBatchWriterBuffer::Invariants() const { |
| // Buffers in buffered_writes_ should not overlap, and collectively they |
| // should cover a continuous prefix of buffer_. |
| const char* next_buffer = buffer_; |
| for (auto iter = buffered_writes_.begin(); iter != buffered_writes_.end(); |
| ++iter) { |
| if ((iter->buffer != next_buffer) || |
| (iter->buffer + iter->buf_len > buffer_end())) { |
| return false; |
| } |
| next_buffer += iter->buf_len; |
| } |
| |
| return static_cast<size_t>(next_buffer - buffer_) == SizeInUse(); |
| } |
| |
| char* QuicBatchWriterBuffer::GetNextWriteLocation() const { |
| const char* next_loc = |
| buffered_writes_.empty() |
| ? buffer_ |
| : buffered_writes_.back().buffer + buffered_writes_.back().buf_len; |
| if (static_cast<size_t>(buffer_end() - next_loc) < kMaxOutgoingPacketSize) { |
| return nullptr; |
| } |
| return const_cast<char*>(next_loc); |
| } |
| |
| QuicBatchWriterBuffer::PushResult QuicBatchWriterBuffer::PushBufferedWrite( |
| const char* buffer, |
| size_t buf_len, |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const PerPacketOptions* options, |
| uint64_t release_time) { |
| QUICHE_DCHECK(Invariants()); |
| QUICHE_DCHECK_LE(buf_len, kMaxOutgoingPacketSize); |
| |
| PushResult result = {/*succeeded=*/false, /*buffer_copied=*/false}; |
| char* next_write_location = GetNextWriteLocation(); |
| if (next_write_location == nullptr) { |
| return result; |
| } |
| |
| if (buffer != next_write_location) { |
| if (IsExternalBuffer(buffer, buf_len)) { |
| memcpy(next_write_location, buffer, buf_len); |
| } else if (IsInternalBuffer(buffer, buf_len)) { |
| memmove(next_write_location, buffer, buf_len); |
| } else { |
| QUIC_BUG(quic_bug_10831_1) |
| << "Buffer[" << static_cast<const void*>(buffer) << ", " |
| << static_cast<const void*>(buffer + buf_len) |
| << ") overlaps with internal buffer[" |
| << static_cast<const void*>(buffer_) << ", " |
| << static_cast<const void*>(buffer_end()) << ")"; |
| return result; |
| } |
| result.buffer_copied = true; |
| } else { |
| // In place push, do nothing. |
| } |
| buffered_writes_.emplace_back( |
| next_write_location, buf_len, self_address, peer_address, |
| options ? options->Clone() : std::unique_ptr<PerPacketOptions>(), |
| release_time); |
| |
| QUICHE_DCHECK(Invariants()); |
| |
| result.succeeded = true; |
| return result; |
| } |
| |
| void QuicBatchWriterBuffer::UndoLastPush() { |
| if (!buffered_writes_.empty()) { |
| buffered_writes_.pop_back(); |
| } |
| } |
| |
| QuicBatchWriterBuffer::PopResult QuicBatchWriterBuffer::PopBufferedWrite( |
| int32_t num_buffered_writes) { |
| QUICHE_DCHECK(Invariants()); |
| QUICHE_DCHECK_GE(num_buffered_writes, 0); |
| QUICHE_DCHECK_LE(static_cast<size_t>(num_buffered_writes), |
| buffered_writes_.size()); |
| |
| PopResult result = {/*num_buffers_popped=*/0, |
| /*moved_remaining_buffers=*/false}; |
| |
| result.num_buffers_popped = std::max<int32_t>(num_buffered_writes, 0); |
| result.num_buffers_popped = |
| std::min<int32_t>(result.num_buffers_popped, buffered_writes_.size()); |
| buffered_writes_.pop_front_n(result.num_buffers_popped); |
| |
| if (!buffered_writes_.empty()) { |
| // If not all buffered writes are erased, the remaining ones will not cover |
| // a continuous prefix of buffer_. We'll fix it by moving the remaining |
| // buffers to the beginning of buffer_ and adjust the buffer pointers in all |
| // remaining buffered writes. |
| // This should happen very rarely, about once per write block. |
| result.moved_remaining_buffers = true; |
| const char* buffer_before_move = buffered_writes_.front().buffer; |
| size_t buffer_len_to_move = buffered_writes_.back().buffer + |
| buffered_writes_.back().buf_len - |
| buffer_before_move; |
| memmove(buffer_, buffer_before_move, buffer_len_to_move); |
| |
| size_t distance_to_move = buffer_before_move - buffer_; |
| for (BufferedWrite& buffered_write : buffered_writes_) { |
| buffered_write.buffer -= distance_to_move; |
| } |
| |
| QUICHE_DCHECK_EQ(buffer_, buffered_writes_.front().buffer); |
| } |
| QUICHE_DCHECK(Invariants()); |
| |
| return result; |
| } |
| |
| size_t QuicBatchWriterBuffer::SizeInUse() const { |
| if (buffered_writes_.empty()) { |
| return 0; |
| } |
| |
| return buffered_writes_.back().buffer + buffered_writes_.back().buf_len - |
| buffer_; |
| } |
| |
| } // namespace quic |