Refactor BinaryHttpRequest IndeterminateLengthDecoder to use a local checkpoint.

This change removes the `checkpoint_view_` member variable from `IndeterminateLengthDecoder`. Instead, a local `absl::string_view` variable is used as the checkpoint and passed by reference between decoding methods. This makes the checkpoint state more explicit within the scope of a single `Decode` call.

The logic for handling potentially truncated body and trailer sections has also been simplified by directly checking if the `QuicheDataReader` is done reading, removing the need for the `maybe_truncated_` member.

PiperOrigin-RevId: 852805787
diff --git a/quiche/binary_http/binary_http_message.cc b/quiche/binary_http/binary_http_message.cc
index c0371ac..3e03806 100644
--- a/quiche/binary_http/binary_http_message.cc
+++ b/quiche/binary_http/binary_http_message.cc
@@ -279,6 +279,33 @@
   return absl::OkStatus();
 }
 
+// Initializes the checkpoint based on the provided data and any buffered data.
+// If the buffer has data, the new data is appended to the buffer.
+absl::string_view InitializeChunkedDecodingCheckpoint(absl::string_view data,
+                                                      std::string& buffer) {
+  absl::string_view checkpoint = data;
+  // Prepend buffered data if present.
+  if (!buffer.empty()) {
+    absl::StrAppend(&buffer, data);
+    checkpoint = buffer;
+  }
+  return checkpoint;
+}
+
+// Updates the checkpoint based on the current position of the reader.
+void UpdateChunkedDecodingCheckpoint(const QuicheDataReader& reader,
+                                     absl::string_view& checkpoint) {
+  checkpoint = reader.PeekRemainingPayload();
+}
+
+// Buffers the checkpoint.
+void BufferChunkedDecodingCheckpoint(absl::string_view checkpoint,
+                                     std::string& buffer) {
+  if (buffer != checkpoint) {
+    buffer.assign(checkpoint);
+  }
+}
+
 void BinaryHttpMessage::Fields::AddField(BinaryHttpMessage::Field field) {
   fields_.push_back(std::move(field));
 }
@@ -502,7 +529,7 @@
 
 absl::Status
 BinaryHttpRequest::IndeterminateLengthDecoder::DecodeContentTerminatedSection(
-    QuicheDataReader& reader) {
+    QuicheDataReader& reader, absl::string_view& checkpoint) {
   uint64_t length_or_content_terminator;
   do {
     if (!reader.ReadVarInt62(&length_or_content_terminator)) {
@@ -558,8 +585,8 @@
       }
     }
     // Either a section was successfully decoded or a content terminator was
-    // encountered, save the checkpoint.
-    SaveCheckpoint(reader);
+    // encountered, update the checkpoint.
+    UpdateChunkedDecodingCheckpoint(reader, checkpoint);
   } while (length_or_content_terminator != kContentTerminator);
   return absl::OkStatus();
 }
@@ -569,8 +596,8 @@
 // return are errors.
 absl::Status
 BinaryHttpRequest::IndeterminateLengthDecoder::DecodeCheckpointData(
-    bool end_stream) {
-  QuicheDataReader reader(checkpoint_view_);
+    bool end_stream, absl::string_view& checkpoint) {
+  QuicheDataReader reader(checkpoint);
   switch (current_section_) {
     case IndeterminateLengthMessageSection::kEnd:
       return absl::InternalError("Decoder is invalid.");
@@ -597,12 +624,13 @@
         return absl::InternalError(absl::StrCat(
             "Failed to handle control data: ", section_status.message()));
       }
-      SaveCheckpoint(reader);
+      UpdateChunkedDecodingCheckpoint(reader, checkpoint);
       current_section_ = IndeterminateLengthMessageSection::kHeader;
     }
       ABSL_FALLTHROUGH_INTENDED;
     case IndeterminateLengthMessageSection::kHeader: {
-      const absl::Status status = DecodeContentTerminatedSection(reader);
+      const absl::Status status =
+          DecodeContentTerminatedSection(reader, checkpoint);
       if (!status.ok()) {
         return status;
       }
@@ -616,13 +644,10 @@
     }
       ABSL_FALLTHROUGH_INTENDED;
     case IndeterminateLengthMessageSection::kBody: {
-      if (!reader.IsDoneReading()) {
-        maybe_truncated_ = false;
-      }
       // Body and trailers truncation is valid only if:
       // 1. There is no data to read after the headers section.
       // 2. This is signaled as the last piece of data (end_stream).
-      if (maybe_truncated_ && end_stream) {
+      if (reader.IsDoneReading() && end_stream) {
         absl::Status section_status =
             message_section_handler_.OnBodyChunksDone();
         if (!section_status.ok()) {
@@ -637,7 +662,8 @@
         return absl::OkStatus();
       }
 
-      absl::Status section_status = DecodeContentTerminatedSection(reader);
+      absl::Status section_status =
+          DecodeContentTerminatedSection(reader, checkpoint);
       if (!section_status.ok()) {
         return section_status;
       }
@@ -647,18 +673,13 @@
             "Failed to handle body chunks done: ", section_status.message()));
       }
       current_section_ = IndeterminateLengthMessageSection::kTrailer;
-      // Reset the truncation flag before entering the trailers section.
-      maybe_truncated_ = true;
     }
       ABSL_FALLTHROUGH_INTENDED;
     case IndeterminateLengthMessageSection::kTrailer: {
-      if (!reader.IsDoneReading()) {
-        maybe_truncated_ = false;
-      }
       // Trailers truncation is valid only if:
       // 1. There is no data to read after the body section.
       // 2. This is signaled as the last piece of data (end_stream).
-      if (maybe_truncated_ && end_stream) {
+      if (reader.IsDoneReading() && end_stream) {
         const absl::Status section_status =
             message_section_handler_.OnTrailersDone();
         if (!section_status.ok()) {
@@ -668,7 +689,8 @@
         return absl::OkStatus();
       }
 
-      absl::Status section_status = DecodeContentTerminatedSection(reader);
+      absl::Status section_status =
+          DecodeContentTerminatedSection(reader, checkpoint);
       if (!section_status.ok()) {
         return section_status;
       }
@@ -693,25 +715,15 @@
       "Unexpected IndeterminateLengthMessageSection value.");
 }
 
-void BinaryHttpRequest::IndeterminateLengthDecoder::InitializeCheckpoint(
-    absl::string_view data) {
-  checkpoint_view_ = data;
-  // Prepend buffered data if present. This is the data from a previous call to
-  // Decode that could not finish because it needed this new data.
-  if (!buffer_.empty()) {
-    absl::StrAppend(&buffer_, data);
-    checkpoint_view_ = buffer_;
-  }
-}
-
 absl::Status BinaryHttpRequest::IndeterminateLengthDecoder::Decode(
     absl::string_view data, bool end_stream) {
   if (current_section_ == IndeterminateLengthMessageSection::kEnd) {
     return absl::InternalError("Decoder is invalid.");
   }
 
-  InitializeCheckpoint(data);
-  absl::Status status = DecodeCheckpointData(end_stream);
+  absl::string_view checkpoint =
+      InitializeChunkedDecodingCheckpoint(data, buffer_);
+  absl::Status status = DecodeCheckpointData(end_stream, checkpoint);
   if (end_stream) {
     current_section_ = IndeterminateLengthMessageSection::kEnd;
     buffer_.clear();
@@ -723,7 +735,7 @@
     return status;
   }
   if (absl::IsOutOfRange(status)) {
-    BufferCheckpoint();
+    BufferChunkedDecodingCheckpoint(checkpoint, buffer_);
     return absl::OkStatus();
   }
   if (!status.ok()) {
diff --git a/quiche/binary_http/binary_http_message.h b/quiche/binary_http/binary_http_message.h
index be23ee0..7994132 100644
--- a/quiche/binary_http/binary_http_message.h
+++ b/quiche/binary_http/binary_http_message.h
@@ -254,40 +254,22 @@
   absl::Status Decode(absl::string_view data, bool end_stream);
 
  private:
-  // Initializes the checkpoint with the provided data and any buffered data.
-  void InitializeCheckpoint(absl::string_view data);
   // Carries out the decode logic from the checkpoint. Returns
   // OutOfRangeError if there is not enough data to decode the current
   // section. When a section is fully decoded, the checkpoint is updated.
-  absl::Status DecodeCheckpointData(bool end_stream);
-  // Saves the checkpoint based on the current position of the reader.
-  void SaveCheckpoint(const QuicheDataReader& reader) {
-    checkpoint_view_ = reader.PeekRemainingPayload();
-  }
-  // Buffers the checkpoint.
-  void BufferCheckpoint() { buffer_ = std::string(checkpoint_view_); }
+  absl::Status DecodeCheckpointData(bool end_stream,
+                                    absl::string_view& checkpoint);
   // Decodes a section 0 or more times until a content terminator is
   // encountered.
-  absl::Status DecodeContentTerminatedSection(QuicheDataReader& reader);
+  absl::Status DecodeContentTerminatedSection(QuicheDataReader& reader,
+                                              absl::string_view& checkpoint);
 
   MessageSectionHandler& message_section_handler_;
   // Stores the data that could not be processed due to missing data.
   std::string buffer_;
-  // Tracks the remaining data to be processed or buffered.
-  // When decoding fails due to missing data, we buffer based on this
-  // checkpoint and return. When decoding succeeds, we update the checkpoint
-  // to not buffer the already processed data.
-  absl::string_view checkpoint_view_;
   // The current section that is being decoded.
   IndeterminateLengthMessageSection current_section_ =
       IndeterminateLengthMessageSection::kControlData;
-  // Upon initial entry of the body or trailer section, the message is assumed
-  // to be truncated. This will be set to `false` upon the detection of data,
-  // and the state remains consistent for the remainder of the section. This
-  // serves to differentiate between true truncation and an `end_stream`
-  // occurring after partial processing of the section's content but before
-  // its content terminator.
-  bool maybe_truncated_ = true;
 };
 
 // Provides encoding methods for an Indeterminate-Length BHTTP request. The