blob: ac7ddd793c55b23f1f62f3846d0f869e0f28f82f [file] [log] [blame]
// 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 "quiche/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