| // 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 |