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