blob: 1c15c2d95f8227cdb3e1c15cebea3c067a90de34 [file] [log] [blame]
// Copyright (c) 2017 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/quic_stream_send_buffer_base.h"
#include <cstddef>
#include "quiche/quic/core/quic_interval.h"
#include "quiche/quic/core/quic_interval_set.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_buffer_allocator.h"
#include "quiche/common/quiche_mem_slice.h"
namespace quic {
bool StreamPendingRetransmission::operator==(
const StreamPendingRetransmission& other) const {
return offset == other.offset && length == other.length;
}
void QuicStreamSendBufferBase::OnStreamDataConsumed(size_t bytes_consumed) {
stream_bytes_written_ += bytes_consumed;
stream_bytes_outstanding_ += bytes_consumed;
}
bool QuicStreamSendBufferBase::OnStreamDataAcked(
QuicStreamOffset offset, QuicByteCount data_length,
QuicByteCount* newly_acked_length) {
QUIC_DVLOG(2) << "Marking data acked at offset " << offset << " length "
<< data_length;
*newly_acked_length = 0;
if (data_length == 0) {
return true;
}
if (bytes_acked_.Empty() || offset >= bytes_acked_.rbegin()->max() ||
bytes_acked_.IsDisjoint(
QuicInterval<QuicStreamOffset>(offset, offset + data_length))) {
// Optimization for the typical case, when all data is newly acked.
if (stream_bytes_outstanding_ < data_length) {
return false;
}
bytes_acked_.AddOptimizedForAppend(offset, offset + data_length);
*newly_acked_length = data_length;
stream_bytes_outstanding_ -= data_length;
pending_retransmissions_.Difference(offset, offset + data_length);
if (!FreeMemSlices(offset, offset + data_length)) {
return false;
}
CleanUpBufferedSlices();
return true;
}
// Exit if no new data gets acked.
if (bytes_acked_.Contains(offset, offset + data_length)) {
return true;
}
// Execute the slow path if newly acked data fill in existing holes.
QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
newly_acked.Difference(bytes_acked_);
for (const auto& interval : newly_acked) {
*newly_acked_length += (interval.max() - interval.min());
}
if (stream_bytes_outstanding_ < *newly_acked_length) {
return false;
}
stream_bytes_outstanding_ -= *newly_acked_length;
bytes_acked_.Add(offset, offset + data_length);
pending_retransmissions_.Difference(offset, offset + data_length);
if (newly_acked.Empty()) {
return true;
}
if (!FreeMemSlices(newly_acked.begin()->min(), newly_acked.rbegin()->max())) {
return false;
}
CleanUpBufferedSlices();
return true;
}
void QuicStreamSendBufferBase::OnStreamDataLost(QuicStreamOffset offset,
QuicByteCount data_length) {
if (data_length == 0) {
return;
}
QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
bytes_lost.Difference(bytes_acked_);
if (bytes_lost.Empty()) {
return;
}
for (const auto& lost : bytes_lost) {
pending_retransmissions_.Add(lost.min(), lost.max());
}
}
void QuicStreamSendBufferBase::OnStreamDataRetransmitted(
QuicStreamOffset offset, QuicByteCount data_length) {
if (data_length == 0) {
return;
}
pending_retransmissions_.Difference(offset, offset + data_length);
}
bool QuicStreamSendBufferBase::HasPendingRetransmission() const {
return !pending_retransmissions_.Empty();
}
StreamPendingRetransmission
QuicStreamSendBufferBase::NextPendingRetransmission() const {
if (HasPendingRetransmission()) {
const auto pending = pending_retransmissions_.begin();
return {pending->min(), pending->max() - pending->min()};
}
QUIC_BUG(quic_bug_10853_3)
<< "NextPendingRetransmission is called unexpected with no "
"pending retransmissions.";
return {0, 0};
}
bool QuicStreamSendBufferBase::IsStreamDataOutstanding(
QuicStreamOffset offset, QuicByteCount data_length) const {
return data_length > 0 &&
!bytes_acked_.Contains(offset, offset + data_length);
}
void QuicStreamSendBufferBase::SetStreamOffsetForTest(
QuicStreamOffset new_offset) {
stream_bytes_written_ = new_offset;
stream_bytes_outstanding_ = new_offset;
}
} // namespace quic