Introduce an inlining version of QuicStreamSendBuffer.
Protected by FLAGS_quic_reloadable_flag_quic_use_inlining_send_buffer2.
PiperOrigin-RevId: 764011313
diff --git a/build/source_list.bzl b/build/source_list.bzl
index fac6ffe..5a8ee78 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -364,6 +364,7 @@
"quic/core/quic_stream_priority.h",
"quic/core/quic_stream_send_buffer.h",
"quic/core/quic_stream_send_buffer_base.h",
+ "quic/core/quic_stream_send_buffer_inlining.h",
"quic/core/quic_stream_sequencer.h",
"quic/core/quic_stream_sequencer_buffer.h",
"quic/core/quic_sustained_bandwidth_recorder.h",
@@ -687,6 +688,7 @@
"quic/core/quic_stream_priority.cc",
"quic/core/quic_stream_send_buffer.cc",
"quic/core/quic_stream_send_buffer_base.cc",
+ "quic/core/quic_stream_send_buffer_inlining.cc",
"quic/core/quic_stream_sequencer.cc",
"quic/core/quic_stream_sequencer_buffer.cc",
"quic/core/quic_sustained_bandwidth_recorder.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 93a4c55..89c4626 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -364,6 +364,7 @@
"src/quiche/quic/core/quic_stream_priority.h",
"src/quiche/quic/core/quic_stream_send_buffer.h",
"src/quiche/quic/core/quic_stream_send_buffer_base.h",
+ "src/quiche/quic/core/quic_stream_send_buffer_inlining.h",
"src/quiche/quic/core/quic_stream_sequencer.h",
"src/quiche/quic/core/quic_stream_sequencer_buffer.h",
"src/quiche/quic/core/quic_sustained_bandwidth_recorder.h",
@@ -687,6 +688,7 @@
"src/quiche/quic/core/quic_stream_priority.cc",
"src/quiche/quic/core/quic_stream_send_buffer.cc",
"src/quiche/quic/core/quic_stream_send_buffer_base.cc",
+ "src/quiche/quic/core/quic_stream_send_buffer_inlining.cc",
"src/quiche/quic/core/quic_stream_sequencer.cc",
"src/quiche/quic/core/quic_stream_sequencer_buffer.cc",
"src/quiche/quic/core/quic_sustained_bandwidth_recorder.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 1cabec8..3a262e2 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -363,6 +363,7 @@
"quiche/quic/core/quic_stream_priority.h",
"quiche/quic/core/quic_stream_send_buffer.h",
"quiche/quic/core/quic_stream_send_buffer_base.h",
+ "quiche/quic/core/quic_stream_send_buffer_inlining.h",
"quiche/quic/core/quic_stream_sequencer.h",
"quiche/quic/core/quic_stream_sequencer_buffer.h",
"quiche/quic/core/quic_sustained_bandwidth_recorder.h",
@@ -686,6 +687,7 @@
"quiche/quic/core/quic_stream_priority.cc",
"quiche/quic/core/quic_stream_send_buffer.cc",
"quiche/quic/core/quic_stream_send_buffer_base.cc",
+ "quiche/quic/core/quic_stream_send_buffer_inlining.cc",
"quiche/quic/core/quic_stream_sequencer.cc",
"quiche/quic/core/quic_stream_sequencer_buffer.cc",
"quiche/quic/core/quic_sustained_bandwidth_recorder.cc",
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 6bb4791..09e6b2a 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -57,6 +57,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_test_peer_addr_change_after_normalize, false, false, "If true, QuicConnection::ProcessValidatedPacket will use normalized address to test peer address changes.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_testonly_default_false, false, false, "A testonly reloadable flag that will always default to false.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_testonly_default_true, true, true, "A testonly reloadable flag that will always default to true.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_use_inlining_send_buffer2, false, false, "Uses an inlining version of QuicSendStreamBuffer.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_use_received_client_addresses_cache, true, true, "If true, use a LRU cache to record client addresses of packets received on server's original address.")
QUICHE_FLAG(bool, quiche_restart_flag_quic_support_release_time_for_gso, false, false, "If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so.")
QUICHE_FLAG(bool, quiche_restart_flag_quic_testonly_default_false, false, false, "A testonly restart flag that will always default to false.")
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc
index 70a6b2e..bc00e87 100644
--- a/quiche/quic/core/http/quic_spdy_stream.cc
+++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -430,6 +430,8 @@
send_buffer().stream_offset() + header.size());
QUIC_DVLOG(1) << ENDPOINT << "Stream " << id()
<< " is writing DATA frame header of length " << header.size();
+ // TODO: b/417402601 - once we always use inlining send buffer, the code below
+ // should always use WriteOrBufferData.
if (can_write) {
// Save one copy and allocation if send buffer can accomodate the header.
quiche::QuicheMemSlice header_slice(std::move(header));
diff --git a/quiche/quic/core/quic_stream.cc b/quiche/quic/core/quic_stream.cc
index 2d28091..63e4253 100644
--- a/quiche/quic/core/quic_stream.cc
+++ b/quiche/quic/core/quic_stream.cc
@@ -5,17 +5,35 @@
#include "quiche/quic/core/quic_stream.h"
#include <algorithm>
+#include <cstdint>
#include <limits>
+#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "quiche/http2/core/spdy_protocol.h"
+#include "quiche/quic/core/frames/quic_connection_close_frame.h"
#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
+#include "quiche/quic/core/frames/quic_rst_stream_frame.h"
+#include "quiche/quic/core/frames/quic_stream_frame.h"
+#include "quiche/quic/core/frames/quic_window_update_frame.h"
+#include "quiche/quic/core/quic_ack_listener_interface.h"
+#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_data_writer.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_flow_controller.h"
+#include "quiche/quic/core/quic_interval_set.h"
#include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_stream_priority.h"
+#include "quiche/quic/core/quic_stream_send_buffer.h"
+#include "quiche/quic/core/quic_stream_send_buffer_base.h"
+#include "quiche/quic/core/quic_stream_send_buffer_inlining.h"
+#include "quiche/quic/core/quic_stream_sequencer.h"
+#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
@@ -24,6 +42,8 @@
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/common/platform/api/quiche_logging.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
+#include "quiche/common/quiche_buffer_allocator.h"
#include "quiche/common/quiche_mem_slice.h"
using spdy::SpdyPriority;
@@ -109,6 +129,17 @@
return DefaultFlowControlWindow(version);
}
+std::unique_ptr<QuicStreamSendBufferBase> CreateSendBuffer(
+ QuicSession* session) {
+ quiche::QuicheBufferAllocator* allocator =
+ session->connection()->helper()->GetStreamSendBufferAllocator();
+ if (GetQuicReloadableFlag(quic_use_inlining_send_buffer2)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_use_inlining_send_buffer2);
+ return std::make_unique<QuicStreamSendBufferInlining>(allocator);
+ }
+ return std::make_unique<QuicStreamSendBuffer>(allocator);
+}
+
} // namespace
PendingStream::PendingStream(QuicStreamId id, QuicSession* session)
@@ -387,8 +418,7 @@
stream_contributes_to_connection_flow_control_(true),
busy_counter_(0),
add_random_padding_after_fin_(false),
- send_buffer_(std::make_unique<QuicStreamSendBuffer>(
- session->connection()->helper()->GetStreamSendBufferAllocator())),
+ send_buffer_(CreateSendBuffer(session)),
buffered_data_threshold_(GetQuicFlag(quic_buffered_data_threshold)),
is_static_(is_static),
deadline_(QuicTime::Zero()),
diff --git a/quiche/quic/core/quic_stream_send_buffer.cc b/quiche/quic/core/quic_stream_send_buffer.cc
index f2b9f51..ad78782 100644
--- a/quiche/quic/core/quic_stream_send_buffer.cc
+++ b/quiche/quic/core/quic_stream_send_buffer.cc
@@ -184,4 +184,13 @@
return last_slice;
}
+QuicByteCount QuicStreamSendBuffer::TotalDataBufferedForTest() {
+ QuicByteCount length = 0;
+ for (auto slice = interval_deque_.DataBegin();
+ slice != interval_deque_.DataEnd(); ++slice) {
+ length += slice->slice.length();
+ }
+ return length;
+}
+
} // namespace quic
diff --git a/quiche/quic/core/quic_stream_send_buffer.h b/quiche/quic/core/quic_stream_send_buffer.h
index fa88e6d..d9b25c3 100644
--- a/quiche/quic/core/quic_stream_send_buffer.h
+++ b/quiche/quic/core/quic_stream_send_buffer.h
@@ -83,6 +83,7 @@
void SetStreamOffsetForTest(QuicStreamOffset new_offset) override;
absl::string_view LatestWriteForTest() override;
+ QuicByteCount TotalDataBufferedForTest() override;
private:
friend class test::QuicStreamSendBufferPeer;
diff --git a/quiche/quic/core/quic_stream_send_buffer_base.h b/quiche/quic/core/quic_stream_send_buffer_base.h
index b2d32ff..9a14b34 100644
--- a/quiche/quic/core/quic_stream_send_buffer_base.h
+++ b/quiche/quic/core/quic_stream_send_buffer_base.h
@@ -114,6 +114,7 @@
virtual void SetStreamOffsetForTest(QuicStreamOffset new_offset);
virtual absl::string_view LatestWriteForTest() = 0;
+ virtual QuicByteCount TotalDataBufferedForTest() = 0;
private:
friend class test::QuicStreamSendBufferPeer;
diff --git a/quiche/quic/core/quic_stream_send_buffer_inlining.cc b/quiche/quic/core/quic_stream_send_buffer_inlining.cc
new file mode 100644
index 0000000..bd4924e
--- /dev/null
+++ b/quiche/quic/core/quic_stream_send_buffer_inlining.cc
@@ -0,0 +1,232 @@
+// 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_inlining.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_interval.h"
+#include "quiche/quic/core/quic_interval_set.h"
+#include "quiche/quic/core/quic_stream_send_buffer_base.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_flags.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 {
+
+namespace {
+
+struct CompareOffset {
+ bool operator()(const BufferedSliceInlining& slice,
+ QuicStreamOffset offset) const {
+ return slice.offset + slice.slice.size() < offset;
+ }
+};
+
+constexpr bool WillInline(absl::string_view data) {
+ return data.size() <= kSendBufferMaxInlinedSize;
+}
+
+} // namespace
+
+BufferedSliceInlining::BufferedSliceInlining(absl::string_view slice,
+ QuicStreamOffset offset)
+ : slice(slice), offset(offset) {}
+
+BufferedSliceInlining::BufferedSliceInlining(BufferedSliceInlining&& other) =
+ default;
+
+BufferedSliceInlining& BufferedSliceInlining::operator=(
+ BufferedSliceInlining&& other) = default;
+
+BufferedSliceInlining::~BufferedSliceInlining() {}
+
+QuicInterval<std::size_t> BufferedSliceInlining::interval() const {
+ const std::size_t length = slice.size();
+ return QuicInterval<std::size_t>(offset, offset + length);
+}
+
+QuicStreamSendBufferInlining::QuicStreamSendBufferInlining(
+ quiche::QuicheBufferAllocator* allocator)
+ : allocator_(allocator) {}
+
+void QuicStreamSendBufferInlining::SaveStreamData(absl::string_view data) {
+ QUIC_DVLOG(2) << "Save stream data offset " << stream_offset_ << " length "
+ << data.length();
+ QUICHE_DCHECK(!data.empty());
+
+ if (WillInline(data)) {
+ // Skip memory allocation for inlined writes.
+ SaveMemSlice(quiche::QuicheMemSlice(
+ data.data(), data.size(), +[](absl::string_view) {}));
+ return;
+ }
+
+ // Latch the maximum data slice size.
+ const QuicByteCount max_data_slice_size =
+ GetQuicFlag(quic_send_buffer_max_data_slice_size);
+ while (!data.empty()) {
+ auto slice_len = std::min<absl::string_view::size_type>(
+ data.length(), max_data_slice_size);
+ auto buffer =
+ quiche::QuicheBuffer::Copy(allocator_, data.substr(0, slice_len));
+ SaveMemSlice(quiche::QuicheMemSlice(std::move(buffer)));
+
+ data = data.substr(slice_len);
+ }
+}
+
+void QuicStreamSendBufferInlining::SaveMemSlice(quiche::QuicheMemSlice slice) {
+ QUIC_DVLOG(2) << "Save slice offset " << stream_offset_ << " length "
+ << slice.length();
+ if (slice.empty()) {
+ QUIC_BUG(quic_bug_10853_1) << "Try to save empty MemSlice to send buffer.";
+ return;
+ }
+ const absl::string_view data = slice.AsStringView();
+ const bool is_inlined = WillInline(data);
+ interval_deque_.PushBack(BufferedSliceInlining(data, stream_offset_));
+ QUICHE_DCHECK_EQ(interval_deque_.DataAt(stream_offset_)->slice.IsInlined(),
+ is_inlined);
+ if (!is_inlined) {
+ auto [it, success] =
+ owned_slices_.emplace(stream_offset_, std::move(slice));
+ QUICHE_DCHECK(success);
+ }
+ stream_offset_ += data.size();
+}
+
+QuicByteCount QuicStreamSendBufferInlining::SaveMemSliceSpan(
+ absl::Span<quiche::QuicheMemSlice> span) {
+ QuicByteCount total = 0;
+ for (quiche::QuicheMemSlice& slice : span) {
+ if (slice.empty()) {
+ // Skip empty slices.
+ continue;
+ }
+ total += slice.length();
+ SaveMemSlice(std::move(slice));
+ }
+ return total;
+}
+
+bool QuicStreamSendBufferInlining::WriteStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ // The iterator returned from |interval_deque_| will automatically advance
+ // the internal write index for the QuicIntervalDeque. The incrementing is
+ // done in operator++.
+ for (auto slice_it = interval_deque_.DataAt(offset);
+ slice_it != interval_deque_.DataEnd(); ++slice_it) {
+ if (data_length == 0 || offset < slice_it->offset) {
+ break;
+ }
+
+ QuicByteCount slice_offset = offset - slice_it->offset;
+ QuicByteCount available_bytes_in_slice =
+ slice_it->slice.size() - slice_offset;
+ QuicByteCount copy_length = std::min(data_length, available_bytes_in_slice);
+ if (!writer->WriteBytes(slice_it->slice.data() + slice_offset,
+ copy_length)) {
+ QUIC_BUG(quic_bug_10853_2) << "Writer fails to write.";
+ return false;
+ }
+ offset += copy_length;
+ data_length -= copy_length;
+ }
+ return data_length == 0;
+}
+
+bool QuicStreamSendBufferInlining::FreeMemSlices(QuicStreamOffset start,
+ QuicStreamOffset end) {
+ auto it = interval_deque_.DataBegin();
+ if (it == interval_deque_.DataEnd() || it->slice.empty()) {
+ QUIC_BUG(quic_bug_10853_4)
+ << "Trying to ack stream data [" << start << ", " << end << "), "
+ << (it == interval_deque_.DataEnd()
+ ? "and there is no outstanding data."
+ : "and the first slice is empty.");
+ return false;
+ }
+ if (!it->interval().Contains(start)) {
+ // Slow path that not the earliest outstanding data gets acked.
+ it = std::lower_bound(interval_deque_.DataBegin(),
+ interval_deque_.DataEnd(), start, CompareOffset());
+ }
+ if (it == interval_deque_.DataEnd() || it->slice.empty()) {
+ QUIC_BUG(quic_bug_10853_5)
+ << "Offset " << start << " with iterator offset: " << it->offset
+ << (it == interval_deque_.DataEnd() ? " does not exist."
+ : " has already been acked.");
+ return false;
+ }
+ for (; it != interval_deque_.DataEnd(); ++it) {
+ if (it->offset >= end) {
+ break;
+ }
+ if (!it->slice.empty() &&
+ bytes_acked().Contains(it->offset, it->offset + it->slice.size())) {
+ ClearSlice(*it);
+ }
+ }
+ return true;
+}
+
+void QuicStreamSendBufferInlining::CleanUpBufferedSlices() {
+ while (!interval_deque_.Empty() &&
+ interval_deque_.DataBegin()->slice.empty()) {
+ interval_deque_.PopFront();
+ }
+}
+
+size_t QuicStreamSendBufferInlining::size() const {
+ return interval_deque_.Size();
+}
+
+void QuicStreamSendBufferInlining::SetStreamOffsetForTest(
+ QuicStreamOffset new_offset) {
+ QuicStreamSendBufferBase::SetStreamOffsetForTest(new_offset);
+ stream_offset_ = new_offset;
+}
+
+absl::string_view QuicStreamSendBufferInlining::LatestWriteForTest() {
+ absl::string_view last_slice = "";
+ for (auto it = interval_deque_.DataBegin(); it != interval_deque_.DataEnd();
+ ++it) {
+ last_slice = it->slice.view();
+ }
+ return last_slice;
+}
+
+void QuicStreamSendBufferInlining::ClearSlice(BufferedSliceInlining& slice) {
+ if (slice.slice.empty()) {
+ return;
+ }
+ const bool was_inlined = slice.slice.IsInlined();
+ slice.slice.clear();
+ if (!was_inlined) {
+ bool deleted = owned_slices_.erase(slice.offset);
+ QUICHE_DCHECK(deleted);
+ }
+}
+
+QuicByteCount QuicStreamSendBufferInlining::TotalDataBufferedForTest() {
+ QuicByteCount length = 0;
+ for (auto slice = interval_deque_.DataBegin();
+ slice != interval_deque_.DataEnd(); ++slice) {
+ length += slice->slice.size();
+ }
+ return length;
+}
+
+} // namespace quic
diff --git a/quiche/quic/core/quic_stream_send_buffer_inlining.h b/quiche/quic/core/quic_stream_send_buffer_inlining.h
new file mode 100644
index 0000000..6cb0f95
--- /dev/null
+++ b/quiche/quic/core/quic_stream_send_buffer_inlining.h
@@ -0,0 +1,126 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_INLINING_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_INLINING_H_
+
+#include <cstddef>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "quiche/quic/core/quic_inlined_string_view.h"
+#include "quiche/quic/core/quic_interval.h"
+#include "quiche/quic/core/quic_interval_deque.h"
+#include "quiche/quic/core/quic_stream_send_buffer_base.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/quiche_buffer_allocator.h"
+#include "quiche/common/quiche_mem_slice.h"
+
+namespace quic {
+
+namespace test {
+class QuicStreamSendBufferPeer;
+}
+
+class QuicDataWriter;
+
+constexpr size_t kSendBufferMaxInlinedSize = 15;
+
+// BufferedSliceInlining is an entry in the send buffer. It contains a pointer
+// to the buffered data (or data itself, if it is inlined), the size of the data
+// and the offset in the buffer.
+//
+// BufferedSliceInlining does not own contents of the slice; those are freed
+// separately. Since we perform a search over an array of BufferedSliceInlining,
+// it is important for this data structure to be compact.
+struct QUICHE_EXPORT BufferedSliceInlining {
+ BufferedSliceInlining(absl::string_view slice, QuicStreamOffset offset);
+ BufferedSliceInlining(BufferedSliceInlining&& other);
+ BufferedSliceInlining& operator=(BufferedSliceInlining&& other);
+
+ BufferedSliceInlining(const BufferedSliceInlining& other) = delete;
+ BufferedSliceInlining& operator=(const BufferedSliceInlining& other) = delete;
+ ~BufferedSliceInlining();
+
+ // Return an interval representing the offset and length.
+ QuicInterval<std::size_t> interval() const;
+
+ // Stream data of this data slice.
+ QuicInlinedStringView<kSendBufferMaxInlinedSize + 1> slice;
+
+ // Location of this data slice in the stream.
+ QuicStreamOffset offset;
+};
+
+// QuicStreamSendBuffer contains all of the outstanding (provided by the
+// application and not yet acknowledged by the peer) stream data. Internally it
+// is a circular deque of (potentially inlined) QuicheMemSlices, indexed by the
+// offset in the stream. The stream can be accessed randomly in O(log(n)) time,
+// though if the offsets are accessed sequentially, the access will be O(1).
+class QUICHE_EXPORT QuicStreamSendBufferInlining
+ : public QuicStreamSendBufferBase {
+ public:
+ explicit QuicStreamSendBufferInlining(
+ quiche::QuicheBufferAllocator* allocator);
+
+ // Save |data| to send buffer.
+ void SaveStreamData(absl::string_view data) override;
+
+ // Save |slice| to send buffer.
+ void SaveMemSlice(quiche::QuicheMemSlice slice) override;
+
+ // Save all slices in |span| to send buffer. Return total bytes saved.
+ QuicByteCount SaveMemSliceSpan(
+ absl::Span<quiche::QuicheMemSlice> span) override;
+
+ // Write |data_length| of data starts at |offset|. Returns true if all data
+ // was successfully written. Returns false if the writer fails to write, or if
+ // the data was already marked as acked, or if the data was never saved in the
+ // first place.
+ bool WriteStreamData(QuicStreamOffset offset, QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+
+ // Number of data slices in send buffer.
+ size_t size() const override;
+
+ QuicStreamOffset stream_offset() const override { return stream_offset_; }
+
+ void SetStreamOffsetForTest(QuicStreamOffset new_offset) override;
+ absl::string_view LatestWriteForTest() override;
+ QuicByteCount TotalDataBufferedForTest() override;
+
+ private:
+ friend class test::QuicStreamSendBufferPeer;
+
+ // Called when data within offset [start, end) gets acked. Frees fully
+ // acked buffered slices if any. Returns false if the corresponding data does
+ // not exist or has been acked.
+ bool FreeMemSlices(QuicStreamOffset start, QuicStreamOffset end) override;
+
+ // Cleanup acked data from the start of the interval.
+ void CleanUpBufferedSlices() override;
+
+ // Frees an individual buffered slice.
+ void ClearSlice(BufferedSliceInlining& slice);
+
+ // Contains actual stream data.
+ QuicIntervalDeque<BufferedSliceInlining> interval_deque_;
+
+ // Offset of next inserted byte.
+ QuicStreamOffset stream_offset_ = 0;
+
+ // For slices that are not inlined, contains a map from the offset of the
+ // slice in the buffer to the slice release callback. Those are stored
+ // separately from `interval_deque_`, since the callbacks themselves can be
+ // quite large, and for many slices, those would not be present.
+ absl::flat_hash_map<QuicStreamOffset, quiche::QuicheMemSlice> owned_slices_;
+
+ quiche::QuicheBufferAllocator* allocator_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_INLINING_H_
diff --git a/quiche/quic/core/quic_stream_send_buffer_test.cc b/quiche/quic/core/quic_stream_send_buffer_test.cc
index d74a757..312f502 100644
--- a/quiche/quic/core/quic_stream_send_buffer_test.cc
+++ b/quiche/quic/core/quic_stream_send_buffer_test.cc
@@ -4,29 +4,58 @@
#include "quiche/quic/core/quic_stream_send_buffer.h"
+#include <cstddef>
+#include <cstring>
+#include <memory>
#include <string>
#include <utility>
#include <vector>
+#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "quiche/quic/core/quic_data_writer.h"
+#include "quiche/quic/core/quic_stream_send_buffer_base.h"
+#include "quiche/quic/core/quic_stream_send_buffer_inlining.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_expect_bug.h"
#include "quiche/quic/platform/api/quic_flags.h"
-#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_stream_send_buffer_peer.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/quiche_buffer_allocator.h"
+#include "quiche/common/quiche_endian.h"
+#include "quiche/common/quiche_mem_slice.h"
#include "quiche/common/simple_buffer_allocator.h"
namespace quic {
namespace test {
namespace {
-class QuicStreamSendBufferTest : public QuicTest {
+enum class SendBufferType {
+ kDefault,
+ kInlining,
+};
+
+std::string SendBufferTypeName(
+ const testing::TestParamInfo<SendBufferType>& type) {
+ switch (type.param) {
+ case SendBufferType::kDefault:
+ return "Default";
+ case SendBufferType::kInlining:
+ return "Inlining";
+ }
+ return "<invalid>";
+}
+
+class QuicStreamSendBufferTest
+ : public quiche::test::QuicheTestWithParam<SendBufferType> {
public:
- QuicStreamSendBufferTest() : send_buffer_(&allocator_) {
- EXPECT_EQ(0u, send_buffer_.size());
- EXPECT_EQ(0u, send_buffer_.stream_bytes_written());
- EXPECT_EQ(0u, send_buffer_.stream_bytes_outstanding());
+ QuicStreamSendBufferTest() {
+ send_buffer_ = CreateBuffer();
+ EXPECT_EQ(0u, send_buffer_->size());
+ EXPECT_EQ(0u, send_buffer_->stream_bytes_written());
+ EXPECT_EQ(0u, send_buffer_->stream_bytes_outstanding());
std::string data1 = absl::StrCat(
std::string(1536, 'a'), std::string(256, 'b'), std::string(256, 'c'));
@@ -41,37 +70,52 @@
// `data` will be split into two BufferedSlices.
SetQuicFlag(quic_send_buffer_max_data_slice_size, 1024);
- send_buffer_.SaveStreamData(data1);
+ send_buffer_->SaveStreamData(data1);
- send_buffer_.SaveMemSlice(std::move(slice1));
+ send_buffer_->SaveMemSlice(std::move(slice1));
EXPECT_TRUE(slice1.empty());
- send_buffer_.SaveMemSlice(std::move(slice2));
+ send_buffer_->SaveMemSlice(std::move(slice2));
EXPECT_TRUE(slice2.empty());
- EXPECT_EQ(4u, send_buffer_.size());
- // At this point, `send_buffer_.interval_deque_` looks like this:
+ EXPECT_EQ(4u, send_buffer_->size());
+ // At this point, `send_buffer_->interval_deque_` looks like this:
// BufferedSlice1: 'a' * 1024
// BufferedSlice2: 'a' * 512 + 'b' * 256 + 'c' * 256
// BufferedSlice3: 'c' * 1024
// BufferedSlice4: 'd' * 768
}
+ std::unique_ptr<QuicStreamSendBufferBase> CreateBuffer() {
+ switch (GetParam()) {
+ case SendBufferType::kDefault:
+ return std::make_unique<QuicStreamSendBuffer>(&allocator_);
+ case SendBufferType::kInlining:
+ return std::make_unique<QuicStreamSendBufferInlining>(&allocator_);
+ }
+ return nullptr;
+ }
+
void WriteAllData() {
// Write all data.
char buf[4000];
QuicDataWriter writer(4000, buf, quiche::HOST_BYTE_ORDER);
- EXPECT_TRUE(send_buffer_.WriteStreamData(0, 3840u, &writer));
+ EXPECT_TRUE(send_buffer_->WriteStreamData(0, 3840u, &writer));
- send_buffer_.OnStreamDataConsumed(3840u);
- EXPECT_EQ(3840u, send_buffer_.stream_bytes_written());
- EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding());
+ send_buffer_->OnStreamDataConsumed(3840u);
+ EXPECT_EQ(3840u, send_buffer_->stream_bytes_written());
+ EXPECT_EQ(3840u, send_buffer_->stream_bytes_outstanding());
}
quiche::SimpleBufferAllocator allocator_;
- QuicStreamSendBuffer send_buffer_;
+ std::unique_ptr<QuicStreamSendBufferBase> send_buffer_;
};
-TEST_F(QuicStreamSendBufferTest, CopyDataToBuffer) {
+INSTANTIATE_TEST_SUITE_P(QuicStreamSendBufferTests, QuicStreamSendBufferTest,
+ testing::Values(SendBufferType::kDefault,
+ SendBufferType::kInlining),
+ SendBufferTypeName);
+
+TEST_P(QuicStreamSendBufferTest, CopyDataToBuffer) {
char buf[4000];
QuicDataWriter writer(4000, buf, quiche::HOST_BYTE_ORDER);
std::string copy1(1024, 'a');
@@ -80,38 +124,38 @@
std::string copy3(1024, 'c');
std::string copy4(768, 'd');
- ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(0, 1024, &writer));
EXPECT_EQ(copy1, absl::string_view(buf, 1024));
- ASSERT_TRUE(send_buffer_.WriteStreamData(1024, 1024, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(1024, 1024, &writer));
EXPECT_EQ(copy2, absl::string_view(buf + 1024, 1024));
- ASSERT_TRUE(send_buffer_.WriteStreamData(2048, 1024, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(2048, 1024, &writer));
EXPECT_EQ(copy3, absl::string_view(buf + 2048, 1024));
- ASSERT_TRUE(send_buffer_.WriteStreamData(3072, 768, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(3072, 768, &writer));
EXPECT_EQ(copy4, absl::string_view(buf + 3072, 768));
// Test data piece across boundries.
QuicDataWriter writer2(4000, buf, quiche::HOST_BYTE_ORDER);
std::string copy5 =
std::string(536, 'a') + std::string(256, 'b') + std::string(232, 'c');
- ASSERT_TRUE(send_buffer_.WriteStreamData(1000, 1024, &writer2));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(1000, 1024, &writer2));
EXPECT_EQ(copy5, absl::string_view(buf, 1024));
- ASSERT_TRUE(send_buffer_.WriteStreamData(2500, 1024, &writer2));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(2500, 1024, &writer2));
std::string copy6 = std::string(572, 'c') + std::string(452, 'd');
EXPECT_EQ(copy6, absl::string_view(buf + 1024, 1024));
// Invalid data copy.
QuicDataWriter writer3(4000, buf, quiche::HOST_BYTE_ORDER);
- EXPECT_FALSE(send_buffer_.WriteStreamData(3000, 1024, &writer3));
- EXPECT_QUIC_BUG(send_buffer_.WriteStreamData(0, 4000, &writer3),
+ EXPECT_FALSE(send_buffer_->WriteStreamData(3000, 1024, &writer3));
+ EXPECT_QUIC_BUG(send_buffer_->WriteStreamData(0, 4000, &writer3),
"Writer fails to write.");
- send_buffer_.OnStreamDataConsumed(3840);
- EXPECT_EQ(3840u, send_buffer_.stream_bytes_written());
- EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding());
+ send_buffer_->OnStreamDataConsumed(3840);
+ EXPECT_EQ(3840u, send_buffer_->stream_bytes_written());
+ EXPECT_EQ(3840u, send_buffer_->stream_bytes_outstanding());
}
// Regression test for b/143491027.
-TEST_F(QuicStreamSendBufferTest,
+TEST_P(QuicStreamSendBufferTest,
WriteStreamDataContainsBothRetransmissionAndNewData) {
std::string copy1(1024, 'a');
std::string copy2 =
@@ -120,180 +164,185 @@
char buf[6000];
QuicDataWriter writer(6000, buf, quiche::HOST_BYTE_ORDER);
// Write more than one slice.
- EXPECT_EQ(0, QuicStreamSendBufferPeer::write_index(&send_buffer_));
- ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer));
+ if (GetParam() == SendBufferType::kDefault) {
+ EXPECT_EQ(0, QuicStreamSendBufferPeer::write_index(
+ static_cast<QuicStreamSendBuffer*>(send_buffer_.get())));
+ }
+ ASSERT_TRUE(send_buffer_->WriteStreamData(0, 1024, &writer));
EXPECT_EQ(copy1, absl::string_view(buf, 1024));
- EXPECT_EQ(1, QuicStreamSendBufferPeer::write_index(&send_buffer_));
+ if (GetParam() == SendBufferType::kDefault) {
+ EXPECT_EQ(1, QuicStreamSendBufferPeer::write_index(
+ static_cast<QuicStreamSendBuffer*>(send_buffer_.get())));
+ }
// Retransmit the first frame and also send new data.
- ASSERT_TRUE(send_buffer_.WriteStreamData(0, 2048, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(0, 2048, &writer));
EXPECT_EQ(copy1 + copy2, absl::string_view(buf + 1024, 2048));
// Write new data.
- ASSERT_TRUE(send_buffer_.WriteStreamData(2048, 50, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(2048, 50, &writer));
EXPECT_EQ(std::string(50, 'c'), absl::string_view(buf + 1024 + 2048, 50));
- ASSERT_TRUE(send_buffer_.WriteStreamData(2048, 1124, &writer));
+ ASSERT_TRUE(send_buffer_->WriteStreamData(2048, 1124, &writer));
EXPECT_EQ(copy3, absl::string_view(buf + 1024 + 2048 + 50, 1124));
}
-TEST_F(QuicStreamSendBufferTest, RemoveStreamFrame) {
+TEST_P(QuicStreamSendBufferTest, RemoveStreamFrame) {
WriteAllData();
QuicByteCount newly_acked_length;
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1024, 1024, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(1024, 1024, &newly_acked_length));
EXPECT_EQ(1024u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2048, 1024, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2048, 1024, &newly_acked_length));
EXPECT_EQ(1024u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1024, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(0, 1024, &newly_acked_length));
EXPECT_EQ(1024u, newly_acked_length);
// Send buffer is cleaned up in order.
- EXPECT_EQ(1u, send_buffer_.size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3072, 768, &newly_acked_length));
+ EXPECT_EQ(1u, send_buffer_->size());
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(3072, 768, &newly_acked_length));
EXPECT_EQ(768u, newly_acked_length);
- EXPECT_EQ(0u, send_buffer_.size());
+ EXPECT_EQ(0u, send_buffer_->size());
}
-TEST_F(QuicStreamSendBufferTest, RemoveStreamFrameAcrossBoundaries) {
+TEST_P(QuicStreamSendBufferTest, RemoveStreamFrameAcrossBoundaries) {
WriteAllData();
QuicByteCount newly_acked_length;
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2024, 576, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2024, 576, &newly_acked_length));
EXPECT_EQ(576u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(0, 1000, &newly_acked_length));
EXPECT_EQ(1000u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1000, 1024, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(1000, 1024, &newly_acked_length));
EXPECT_EQ(1024u, newly_acked_length);
// Send buffer is cleaned up in order.
- EXPECT_EQ(2u, send_buffer_.size());
+ EXPECT_EQ(2u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2600, 1024, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2600, 1024, &newly_acked_length));
EXPECT_EQ(1024u, newly_acked_length);
- EXPECT_EQ(1u, send_buffer_.size());
+ EXPECT_EQ(1u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3624, 216, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(3624, 216, &newly_acked_length));
EXPECT_EQ(216u, newly_acked_length);
- EXPECT_EQ(0u, send_buffer_.size());
+ EXPECT_EQ(0u, send_buffer_->size());
}
-TEST_F(QuicStreamSendBufferTest, AckStreamDataMultipleTimes) {
+TEST_P(QuicStreamSendBufferTest, AckStreamDataMultipleTimes) {
WriteAllData();
QuicByteCount newly_acked_length;
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(100, 1500, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(100, 1500, &newly_acked_length));
EXPECT_EQ(1500u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 500, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2000, 500, &newly_acked_length));
EXPECT_EQ(500u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 2600, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(0, 2600, &newly_acked_length));
EXPECT_EQ(600u, newly_acked_length);
// Send buffer is cleaned up in order.
- EXPECT_EQ(2u, send_buffer_.size());
+ EXPECT_EQ(2u, send_buffer_->size());
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2200, 1640, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2200, 1640, &newly_acked_length));
EXPECT_EQ(1240u, newly_acked_length);
- EXPECT_EQ(0u, send_buffer_.size());
+ EXPECT_EQ(0u, send_buffer_->size());
- EXPECT_FALSE(send_buffer_.OnStreamDataAcked(4000, 100, &newly_acked_length));
+ EXPECT_FALSE(send_buffer_->OnStreamDataAcked(4000, 100, &newly_acked_length));
}
-TEST_F(QuicStreamSendBufferTest, AckStreamDataOutOfOrder) {
+TEST_P(QuicStreamSendBufferTest, AckStreamDataOutOfOrder) {
WriteAllData();
QuicByteCount newly_acked_length;
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 1000, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(500, 1000, &newly_acked_length));
EXPECT_EQ(1000u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
- EXPECT_EQ(3840u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+ EXPECT_EQ(4u, send_buffer_->size());
+ EXPECT_EQ(3840u, QuicStreamSendBufferPeer::TotalLength(send_buffer_.get()));
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1200, 1000, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(1200, 1000, &newly_acked_length));
EXPECT_EQ(700u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
// Slice 2 gets fully acked.
- EXPECT_EQ(2816u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+ EXPECT_EQ(2816u, QuicStreamSendBufferPeer::TotalLength(send_buffer_.get()));
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 1840, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(2000, 1840, &newly_acked_length));
EXPECT_EQ(1640u, newly_acked_length);
- EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(4u, send_buffer_->size());
// Slices 3 and 4 get fully acked.
- EXPECT_EQ(1024u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+ EXPECT_EQ(1024u, QuicStreamSendBufferPeer::TotalLength(send_buffer_.get()));
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length));
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(0, 1000, &newly_acked_length));
EXPECT_EQ(500u, newly_acked_length);
- EXPECT_EQ(0u, send_buffer_.size());
- EXPECT_EQ(0u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+ EXPECT_EQ(0u, send_buffer_->size());
+ EXPECT_EQ(0u, QuicStreamSendBufferPeer::TotalLength(send_buffer_.get()));
}
-TEST_F(QuicStreamSendBufferTest, PendingRetransmission) {
+TEST_P(QuicStreamSendBufferTest, PendingRetransmission) {
WriteAllData();
- EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 3840));
- EXPECT_FALSE(send_buffer_.HasPendingRetransmission());
+ EXPECT_TRUE(send_buffer_->IsStreamDataOutstanding(0, 3840));
+ EXPECT_FALSE(send_buffer_->HasPendingRetransmission());
// Lost data [0, 1200).
- send_buffer_.OnStreamDataLost(0, 1200);
+ send_buffer_->OnStreamDataLost(0, 1200);
// Lost data [1500, 2000).
- send_buffer_.OnStreamDataLost(1500, 500);
- EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+ send_buffer_->OnStreamDataLost(1500, 500);
+ EXPECT_TRUE(send_buffer_->HasPendingRetransmission());
EXPECT_EQ(StreamPendingRetransmission(0, 1200),
- send_buffer_.NextPendingRetransmission());
+ send_buffer_->NextPendingRetransmission());
// Retransmit data [0, 500).
- send_buffer_.OnStreamDataRetransmitted(0, 500);
- EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 500));
+ send_buffer_->OnStreamDataRetransmitted(0, 500);
+ EXPECT_TRUE(send_buffer_->IsStreamDataOutstanding(0, 500));
EXPECT_EQ(StreamPendingRetransmission(500, 700),
- send_buffer_.NextPendingRetransmission());
+ send_buffer_->NextPendingRetransmission());
// Ack data [500, 1200).
QuicByteCount newly_acked_length = 0;
- EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 700, &newly_acked_length));
- EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(500, 700));
- EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+ EXPECT_TRUE(send_buffer_->OnStreamDataAcked(500, 700, &newly_acked_length));
+ EXPECT_FALSE(send_buffer_->IsStreamDataOutstanding(500, 700));
+ EXPECT_TRUE(send_buffer_->HasPendingRetransmission());
EXPECT_EQ(StreamPendingRetransmission(1500, 500),
- send_buffer_.NextPendingRetransmission());
+ send_buffer_->NextPendingRetransmission());
// Retransmit data [1500, 2000).
- send_buffer_.OnStreamDataRetransmitted(1500, 500);
- EXPECT_FALSE(send_buffer_.HasPendingRetransmission());
+ send_buffer_->OnStreamDataRetransmitted(1500, 500);
+ EXPECT_FALSE(send_buffer_->HasPendingRetransmission());
// Lost [200, 800).
- send_buffer_.OnStreamDataLost(200, 600);
- EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+ send_buffer_->OnStreamDataLost(200, 600);
+ EXPECT_TRUE(send_buffer_->HasPendingRetransmission());
// Verify [200, 500) is considered as lost, as [500, 800) has been acked.
EXPECT_EQ(StreamPendingRetransmission(200, 300),
- send_buffer_.NextPendingRetransmission());
+ send_buffer_->NextPendingRetransmission());
// Verify 0 length data is not outstanding.
- EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(100, 0));
+ EXPECT_FALSE(send_buffer_->IsStreamDataOutstanding(100, 0));
// Verify partially acked data is outstanding.
- EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(400, 800));
+ EXPECT_TRUE(send_buffer_->IsStreamDataOutstanding(400, 800));
}
-TEST_F(QuicStreamSendBufferTest, OutOfOrderWrites) {
+TEST_P(QuicStreamSendBufferTest, OutOfOrderWrites) {
char buf[3840] = {};
// Write data out of order.
QuicDataWriter writer2(sizeof(buf) - 1000, buf + 1000);
- EXPECT_TRUE(send_buffer_.WriteStreamData(1000u, 1000u, &writer2));
+ EXPECT_TRUE(send_buffer_->WriteStreamData(1000u, 1000u, &writer2));
QuicDataWriter writer4(sizeof(buf) - 3000, buf + 3000);
- EXPECT_TRUE(send_buffer_.WriteStreamData(3000u, 840u, &writer4));
+ EXPECT_TRUE(send_buffer_->WriteStreamData(3000u, 840u, &writer4));
QuicDataWriter writer3(sizeof(buf) - 2000, buf + 2000);
- EXPECT_TRUE(send_buffer_.WriteStreamData(2000u, 1000u, &writer3));
+ EXPECT_TRUE(send_buffer_->WriteStreamData(2000u, 1000u, &writer3));
QuicDataWriter writer1(sizeof(buf), buf);
- EXPECT_TRUE(send_buffer_.WriteStreamData(0u, 1000u, &writer1));
+ EXPECT_TRUE(send_buffer_->WriteStreamData(0u, 1000u, &writer1));
// Make sure it is correct.
EXPECT_EQ(absl::string_view(buf, sizeof(buf)),
absl::StrCat(std::string(1536, 'a'), std::string(256, 'b'),
std::string(1280, 'c'), std::string(768, 'd')));
}
-TEST_F(QuicStreamSendBufferTest, SaveMemSliceSpan) {
- quiche::SimpleBufferAllocator allocator;
- QuicStreamSendBuffer send_buffer(&allocator);
+TEST_P(QuicStreamSendBufferTest, SaveMemSliceSpan) {
+ std::unique_ptr<QuicStreamSendBufferBase> send_buffer = CreateBuffer();
std::string data(1024, 'a');
std::vector<quiche::QuicheMemSlice> buffers;
@@ -301,13 +350,12 @@
buffers.push_back(MemSliceFromString(data));
}
- EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(absl::MakeSpan(buffers)));
- EXPECT_EQ(10u, send_buffer.size());
+ EXPECT_EQ(10 * 1024u, send_buffer->SaveMemSliceSpan(absl::MakeSpan(buffers)));
+ EXPECT_EQ(10u, send_buffer->size());
}
-TEST_F(QuicStreamSendBufferTest, SaveEmptyMemSliceSpan) {
- quiche::SimpleBufferAllocator allocator;
- QuicStreamSendBuffer send_buffer(&allocator);
+TEST_P(QuicStreamSendBufferTest, SaveEmptyMemSliceSpan) {
+ std::unique_ptr<QuicStreamSendBufferBase> send_buffer = CreateBuffer();
std::string data(1024, 'a');
std::vector<quiche::QuicheMemSlice> buffers;
@@ -315,9 +363,23 @@
buffers.push_back(MemSliceFromString(data));
}
- EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(absl::MakeSpan(buffers)));
+ EXPECT_EQ(10 * 1024u, send_buffer->SaveMemSliceSpan(absl::MakeSpan(buffers)));
// Verify the empty slice does not get saved.
- EXPECT_EQ(10u, send_buffer.size());
+ EXPECT_EQ(10u, send_buffer->size());
+}
+
+TEST_P(QuicStreamSendBufferTest, SmallWrite) {
+ std::unique_ptr<QuicStreamSendBufferBase> send_buffer = CreateBuffer();
+
+ constexpr absl::string_view kData = "abcd";
+ send_buffer->SaveStreamData(kData);
+ EXPECT_EQ(1u, send_buffer->size());
+ EXPECT_EQ(4u, send_buffer->TotalDataBufferedForTest());
+
+ char buffer[16];
+ QuicDataWriter writer(sizeof(buffer), buffer);
+ ASSERT_TRUE(send_buffer->WriteStreamData(0, 4, &writer));
+ EXPECT_EQ(absl::string_view(buffer, 4), kData);
}
} // namespace
diff --git a/quiche/quic/test_tools/quic_stream_send_buffer_peer.cc b/quiche/quic/test_tools/quic_stream_send_buffer_peer.cc
index c2c2806..35bca77 100644
--- a/quiche/quic/test_tools/quic_stream_send_buffer_peer.cc
+++ b/quiche/quic/test_tools/quic_stream_send_buffer_peer.cc
@@ -29,13 +29,8 @@
// static
QuicByteCount QuicStreamSendBufferPeer::TotalLength(
- QuicStreamSendBuffer* send_buffer) {
- QuicByteCount length = 0;
- for (auto slice = send_buffer->interval_deque_.DataBegin();
- slice != send_buffer->interval_deque_.DataEnd(); ++slice) {
- length += slice->slice.length();
- }
- return length;
+ QuicStreamSendBufferBase* send_buffer) {
+ return send_buffer->TotalDataBufferedForTest();
}
// static
diff --git a/quiche/quic/test_tools/quic_stream_send_buffer_peer.h b/quiche/quic/test_tools/quic_stream_send_buffer_peer.h
index 8969241..d65a1bf 100644
--- a/quiche/quic/test_tools/quic_stream_send_buffer_peer.h
+++ b/quiche/quic/test_tools/quic_stream_send_buffer_peer.h
@@ -19,7 +19,7 @@
static const BufferedSlice* CurrentWriteSlice(
QuicStreamSendBuffer* send_buffer);
- static QuicByteCount TotalLength(QuicStreamSendBuffer* send_buffer);
+ static QuicByteCount TotalLength(QuicStreamSendBufferBase* send_buffer);
static int32_t write_index(QuicStreamSendBuffer* send_buffer);
};