Refactor QpackEncoder to do two-pass encoding.

gfe-relnote: n/a, change in QUIC v99-only code.
PiperOrigin-RevId: 258561443
Change-Id: If03ac2f76064d2b462646bcda07a9e6e98655482
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc
index 412b7af..3ffe9d0 100644
--- a/quic/core/qpack/qpack_encoder.cc
+++ b/quic/core/qpack/qpack_encoder.cc
@@ -4,7 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
 
-#include <string>
+#include <list>
 
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
@@ -29,18 +29,10 @@
 std::string QpackEncoder::EncodeHeaderList(
     QuicStreamId /* stream_id */,
     const spdy::SpdyHeaderBlock* header_list) {
-  QpackInstructionEncoder instruction_encoder;
-  std::string encoded_headers;
+  // First pass.
 
-  // TODO(bnc): Implement dynamic entries and set Required Insert Count and
-  // Delta Base accordingly.
-  QpackInstructionEncoder::Values values;
-  values.varint = 0;
-  values.varint2 = 0;
-  values.s_bit = false;
-
-  instruction_encoder.Encode(QpackPrefixInstruction(), values,
-                             &encoded_headers);
+  // Encode into |instructions| which will be serialized during the second pass.
+  std::list<InstructionWithValues> instructions;
 
   for (const auto& header : ValueSplittingHeaderList(header_list)) {
     QuicStringPiece name = header.first;
@@ -56,36 +48,50 @@
       case QpackHeaderTable::MatchType::kNameAndValue:
         DCHECK(is_static) << "Dynamic table entries not supported yet.";
 
-        values.s_bit = is_static;
-        values.varint = index;
-
-        instruction_encoder.Encode(QpackIndexedHeaderFieldInstruction(), values,
-                                   &encoded_headers);
+        instructions.push_back({QpackIndexedHeaderFieldInstruction(), {}});
+        instructions.back().values.s_bit = is_static;
+        instructions.back().values.varint = index;
 
         break;
       case QpackHeaderTable::MatchType::kName:
         DCHECK(is_static) << "Dynamic table entries not supported yet.";
 
-        values.s_bit = is_static;
-        values.varint = index;
-        values.value = value;
-
-        instruction_encoder.Encode(
-            QpackLiteralHeaderFieldNameReferenceInstruction(), values,
-            &encoded_headers);
+        instructions.push_back(
+            {QpackLiteralHeaderFieldNameReferenceInstruction(), {}});
+        instructions.back().values.s_bit = is_static;
+        instructions.back().values.varint = index;
+        instructions.back().values.value = value;
 
         break;
       case QpackHeaderTable::MatchType::kNoMatch:
-        values.name = name;
-        values.value = value;
-
-        instruction_encoder.Encode(QpackLiteralHeaderFieldInstruction(), values,
-                                   &encoded_headers);
+        instructions.push_back({QpackLiteralHeaderFieldInstruction(), {}});
+        instructions.back().values.name = name;
+        instructions.back().values.value = value;
 
         break;
     }
   }
 
+  // Second pass.
+  QpackInstructionEncoder instruction_encoder;
+  std::string encoded_headers;
+
+  // Header block prefix.
+  // TODO(bnc): Implement dynamic entries and set Required Insert Count and
+  // Delta Base accordingly.
+  QpackInstructionEncoder::Values values;
+  values.varint = 0;     // Encoded required insert count.
+  values.varint2 = 0;    // Delta Base.
+  values.s_bit = false;  // Delta Base sign.
+
+  instruction_encoder.Encode(QpackPrefixInstruction(), values,
+                             &encoded_headers);
+
+  for (const auto& instruction : instructions) {
+    instruction_encoder.Encode(instruction.instruction, instruction.values,
+                               &encoded_headers);
+  }
+
   return encoded_headers;
 }
 
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h
index 7ed5808..39600e8 100644
--- a/quic/core/qpack/qpack_encoder.h
+++ b/quic/core/qpack/qpack_encoder.h
@@ -56,6 +56,17 @@
   void OnErrorDetected(QuicStringPiece error_message) override;
 
  private:
+  // TODO(bnc): Consider moving this class to QpackInstructionEncoder or
+  // qpack_constants, adding factory methods, one for each instruction, and
+  // changing QpackInstructionEncoder::Encoder() to take an
+  // InstructionWithValues struct instead of separate |instruction| and |values|
+  // arguments.
+  struct InstructionWithValues {
+    // |instruction| is not owned.
+    const QpackInstruction* instruction;
+    QpackInstructionEncoder::Values values;
+  };
+
   DecoderStreamErrorDelegate* const decoder_stream_error_delegate_;
   QpackDecoderStreamReceiver decoder_stream_receiver_;
   QpackEncoderStreamSender encoder_stream_sender_;