Implement QuicStreamSequencerBuffer::PeekRegion().
gfe-relnote: n/a, no functional change.
PiperOrigin-RevId: 254856738
Change-Id: I34f1fd30429f8d5d403fa17fc1f4a127c074f952
diff --git a/quic/core/quic_stream_sequencer.cc b/quic/core/quic_stream_sequencer.cc
index 9832fc9..9e47a6f 100644
--- a/quic/core/quic_stream_sequencer.cc
+++ b/quic/core/quic_stream_sequencer.cc
@@ -156,6 +156,12 @@
return buffered_frames_.GetReadableRegion(iov);
}
+bool QuicStreamSequencer::PeekRegion(QuicStreamOffset offset,
+ iovec* iov) const {
+ DCHECK(!blocked_);
+ return buffered_frames_.PeekRegion(offset, iov);
+}
+
bool QuicStreamSequencer::PrefetchNextRegion(iovec* iov) {
DCHECK(!blocked_);
return buffered_frames_.PrefetchNextRegion(iov);
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h
index 7cecf4d..5888a4a 100644
--- a/quic/core/quic_stream_sequencer.h
+++ b/quic/core/quic_stream_sequencer.h
@@ -81,7 +81,12 @@
// is no readable region available.
bool GetReadableRegion(iovec* iov) const;
- // Fill in one iovec with the next unread region for the quic spdy stream.
+ // Fills in one iovec with the region starting at |offset| and returns true.
+ // Returns false if no readable region is available, either because data has
+ // not been received yet or has already been consumed.
+ bool PeekRegion(QuicStreamOffset offset, iovec* iov) const;
+
+ // Fills in one iovec with the next unread region.
// Returns false if no readable region is available.
bool PrefetchNextRegion(iovec* iov);
diff --git a/quic/core/quic_stream_sequencer_buffer.cc b/quic/core/quic_stream_sequencer_buffer.cc
index 47211cc..f4ff276 100644
--- a/quic/core/quic_stream_sequencer_buffer.cc
+++ b/quic/core/quic_stream_sequencer_buffer.cc
@@ -335,18 +335,23 @@
return GetReadableRegions(iov, 1) == 1;
}
-bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) {
- DCHECK(iov != nullptr);
- DCHECK_LE(total_bytes_read_, total_bytes_prefetched_);
- DCHECK_LE(total_bytes_prefetched_, FirstMissingByte());
+bool QuicStreamSequencerBuffer::PeekRegion(QuicStreamOffset offset,
+ iovec* iov) const {
+ DCHECK(iov);
- if (total_bytes_prefetched_ == FirstMissingByte()) {
+ if (offset < total_bytes_read_) {
+ // Data at |offset| has already been consumed.
+ return false;
+ }
+
+ if (offset >= FirstMissingByte()) {
+ // Data at |offset| has not been received yet.
return false;
}
// Beginning of region.
- size_t block_idx = GetBlockIndex(total_bytes_prefetched_);
- size_t block_offset = GetInBlockOffset(total_bytes_prefetched_);
+ size_t block_idx = GetBlockIndex(offset);
+ size_t block_offset = GetInBlockOffset(offset);
iov->iov_base = blocks_[block_idx]->buffer + block_offset;
// Determine if entire block has been received.
@@ -359,6 +364,18 @@
iov->iov_len = GetBlockCapacity(block_idx) - block_offset;
}
+ return true;
+}
+
+bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) {
+ DCHECK(iov);
+ DCHECK_LE(total_bytes_read_, total_bytes_prefetched_);
+ DCHECK_LE(total_bytes_prefetched_, FirstMissingByte());
+
+ if (!PeekRegion(total_bytes_prefetched_, iov)) {
+ return false;
+ }
+
total_bytes_prefetched_ += iov->iov_len;
return true;
}
diff --git a/quic/core/quic_stream_sequencer_buffer.h b/quic/core/quic_stream_sequencer_buffer.h
index 08a1ab4..6347b1c 100644
--- a/quic/core/quic_stream_sequencer_buffer.h
+++ b/quic/core/quic_stream_sequencer_buffer.h
@@ -131,10 +131,17 @@
// Returns false if there is no readable region available.
bool GetReadableRegion(iovec* iov) const;
+ // Returns true and sets |*iov| to point to a region starting at |offset|.
+ // Returns false if no data can be read at |offset|, which can be because data
+ // has not been received yet or it is already consumed.
+ // Does not consume data.
+ bool PeekRegion(QuicStreamOffset offset, iovec* iov) const;
+
+ // DEPRECATED, use PeekRegion() instead.
// Called to return the next region that has not been returned by this method
- // previously.
- // If this method is to be used along with Readv() or MarkConsumed(), make
- // sure that they are consuming less data than is read by this method.
+ // previously, except if Clear() has been called then prefetch offset is set
+ // to BytesConsumed(), and if Readv() or MarkConsumed() reads further than
+ // prefetch offset, then offset is set to beginning of not yet consumed area.
// This method only returns reference of underlying data. The caller is
// responsible for copying and consuming the data.
// Returns true if the data is read, false otherwise.
diff --git a/quic/core/quic_stream_sequencer_buffer_test.cc b/quic/core/quic_stream_sequencer_buffer_test.cc
index 49f83f0..20dbe05 100644
--- a/quic/core/quic_stream_sequencer_buffer_test.cc
+++ b/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -602,14 +602,14 @@
iovec iov;
EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
EXPECT_EQ(source, IovecToStringPiece(iov));
- // The second frame goes into the same bucket.
+ // The second frame goes into the same block.
std::string source2(800, 'a');
buffer_->OnStreamData(1024, source2, &written_, &error_details_);
EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
EXPECT_EQ(source2, IovecToStringPiece(iov));
}
-TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithMultipleBucket) {
+TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithMultipleBlocks) {
const size_t data_size = 1024;
std::string source(data_size, 'a');
buffer_->OnStreamData(0, source, &written_, &error_details_);
@@ -678,6 +678,164 @@
EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov));
}
+TEST_F(QuicStreamSequencerBufferTest, PeekEmptyBuffer) {
+ iovec iov;
+ EXPECT_FALSE(buffer_->PeekRegion(0, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(1, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(100, &iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PeekSingleBlock) {
+ std::string source(kBlockSizeBytes, 'a');
+ buffer_->OnStreamData(0, source, &written_, &error_details_);
+
+ iovec iov;
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source, IovecToStringPiece(iov));
+
+ // Peeking again gives the same result.
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source, IovecToStringPiece(iov));
+
+ // Peek at a different offset.
+ EXPECT_TRUE(buffer_->PeekRegion(100, &iov));
+ EXPECT_EQ(QuicStringPiece(source).substr(100), IovecToStringPiece(iov));
+
+ // Peeking at or after FirstMissingByte() returns false.
+ EXPECT_FALSE(buffer_->PeekRegion(kBlockSizeBytes, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(kBlockSizeBytes + 1, &iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PeekTwoWritesInSingleBlock) {
+ const size_t length1 = 1024;
+ std::string source1(length1, 'a');
+ buffer_->OnStreamData(0, source1, &written_, &error_details_);
+
+ iovec iov;
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source1, IovecToStringPiece(iov));
+
+ // The second frame goes into the same block.
+ const size_t length2 = 800;
+ std::string source2(length2, 'b');
+ buffer_->OnStreamData(length1, source2, &written_, &error_details_);
+
+ EXPECT_TRUE(buffer_->PeekRegion(length1, &iov));
+ EXPECT_EQ(source2, IovecToStringPiece(iov));
+
+ // Peek with an offset inside the first write.
+ const QuicStreamOffset offset1 = 500;
+ EXPECT_TRUE(buffer_->PeekRegion(offset1, &iov));
+ EXPECT_EQ(QuicStringPiece(source1).substr(offset1),
+ IovecToStringPiece(iov).substr(0, length1 - offset1));
+ EXPECT_EQ(QuicStringPiece(source2),
+ IovecToStringPiece(iov).substr(length1 - offset1));
+
+ // Peek with an offset inside the second write.
+ const QuicStreamOffset offset2 = 1500;
+ EXPECT_TRUE(buffer_->PeekRegion(offset2, &iov));
+ EXPECT_EQ(QuicStringPiece(source2).substr(offset2 - length1),
+ IovecToStringPiece(iov));
+
+ // Peeking at or after FirstMissingByte() returns false.
+ EXPECT_FALSE(buffer_->PeekRegion(length1 + length2, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(length1 + length2 + 1, &iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PeekBufferWithMultipleBlocks) {
+ const size_t length1 = 1024;
+ std::string source1(length1, 'a');
+ buffer_->OnStreamData(0, source1, &written_, &error_details_);
+
+ iovec iov;
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source1, IovecToStringPiece(iov));
+
+ const size_t length2 = kBlockSizeBytes + 2;
+ std::string source2(length2, 'b');
+ buffer_->OnStreamData(length1, source2, &written_, &error_details_);
+
+ // Peek with offset 0 returns the entire block.
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(kBlockSizeBytes, iov.iov_len);
+ EXPECT_EQ(source1, IovecToStringPiece(iov).substr(0, length1));
+ EXPECT_EQ(QuicStringPiece(source2).substr(0, kBlockSizeBytes - length1),
+ IovecToStringPiece(iov).substr(length1));
+
+ EXPECT_TRUE(buffer_->PeekRegion(length1, &iov));
+ EXPECT_EQ(QuicStringPiece(source2).substr(0, kBlockSizeBytes - length1),
+ IovecToStringPiece(iov));
+
+ EXPECT_TRUE(buffer_->PeekRegion(kBlockSizeBytes, &iov));
+ EXPECT_EQ(QuicStringPiece(source2).substr(kBlockSizeBytes - length1),
+ IovecToStringPiece(iov));
+
+ // Peeking at or after FirstMissingByte() returns false.
+ EXPECT_FALSE(buffer_->PeekRegion(length1 + length2, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(length1 + length2 + 1, &iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PeekAfterConsumed) {
+ std::string source1(kBlockSizeBytes, 'a');
+ buffer_->OnStreamData(0, source1, &written_, &error_details_);
+
+ iovec iov;
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source1, IovecToStringPiece(iov));
+
+ // Consume some data.
+ EXPECT_TRUE(buffer_->MarkConsumed(1024));
+
+ // Peeking into consumed data fails.
+ EXPECT_FALSE(buffer_->PeekRegion(0, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(512, &iov));
+
+ EXPECT_TRUE(buffer_->PeekRegion(1024, &iov));
+ EXPECT_EQ(QuicStringPiece(source1).substr(1024), IovecToStringPiece(iov));
+
+ EXPECT_TRUE(buffer_->PeekRegion(1500, &iov));
+ EXPECT_EQ(QuicStringPiece(source1).substr(1500), IovecToStringPiece(iov));
+
+ // Consume rest of block.
+ EXPECT_TRUE(buffer_->MarkConsumed(kBlockSizeBytes - 1024));
+
+ // Read new data.
+ std::string source2(300, 'b');
+ buffer_->OnStreamData(kBlockSizeBytes, source2, &written_, &error_details_);
+
+ // Peek into new data.
+ EXPECT_TRUE(buffer_->PeekRegion(kBlockSizeBytes, &iov));
+ EXPECT_EQ(source2, IovecToStringPiece(iov));
+
+ EXPECT_TRUE(buffer_->PeekRegion(kBlockSizeBytes + 128, &iov));
+ EXPECT_EQ(QuicStringPiece(source2).substr(128), IovecToStringPiece(iov));
+
+ // Peeking into consumed data still fails.
+ EXPECT_FALSE(buffer_->PeekRegion(0, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(512, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(1024, &iov));
+ EXPECT_FALSE(buffer_->PeekRegion(1500, &iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PeekContinously) {
+ std::string source1(kBlockSizeBytes, 'a');
+ buffer_->OnStreamData(0, source1, &written_, &error_details_);
+
+ iovec iov;
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source1, IovecToStringPiece(iov));
+
+ std::string source2(kBlockSizeBytes, 'b');
+ buffer_->OnStreamData(kBlockSizeBytes, source2, &written_, &error_details_);
+
+ EXPECT_TRUE(buffer_->PeekRegion(kBlockSizeBytes, &iov));
+ EXPECT_EQ(source2, IovecToStringPiece(iov));
+
+ // First block is still there.
+ EXPECT_TRUE(buffer_->PeekRegion(0, &iov));
+ EXPECT_EQ(source1, IovecToStringPiece(iov));
+}
+
TEST_F(QuicStreamSequencerBufferTest, MarkConsumedInOneBlock) {
// Write into [0, 1024) and then read out [0, 256)
std::string source(1024, 'a');