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); };