Add QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry().

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 260535294
Change-Id: Ica02434e33e5a60d51d297f7837e0726dc1cbd3c
diff --git a/quic/core/qpack/qpack_header_table.cc b/quic/core/qpack/qpack_header_table.cc
index 92eb072..3c9296d 100644
--- a/quic/core/qpack/qpack_header_table.cc
+++ b/quic/core/qpack/qpack_header_table.cc
@@ -141,6 +141,28 @@
   return new_entry;
 }
 
+uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry(
+    uint64_t index) const {
+  DCHECK_LE(dropped_entry_count_, index);
+
+  if (index > inserted_entry_count()) {
+    // All entries are allowed to be evicted.
+    return dynamic_table_capacity_;
+  }
+
+  // Initialize to current available capacity.
+  uint64_t max_insert_size = dynamic_table_capacity_ - dynamic_table_size_;
+
+  for (const auto& entry : dynamic_entries_) {
+    if (entry.InsertionIndex() >= index) {
+      break;
+    }
+    max_insert_size += entry.Size();
+  }
+
+  return max_insert_size;
+}
+
 bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) {
   if (capacity > maximum_dynamic_table_capacity_) {
     return false;
diff --git a/quic/core/qpack/qpack_header_table.h b/quic/core/qpack/qpack_header_table.h
index 54b0829..4e3b77c 100644
--- a/quic/core/qpack/qpack_header_table.h
+++ b/quic/core/qpack/qpack_header_table.h
@@ -70,6 +70,12 @@
   // is larger than the capacity of the dynamic table.
   const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value);
 
+  // Returns the size of the largest entry that could be inserted into the
+  // dynamic table without evicting entry |index|.  |index| might be larger than
+  // inserted_entry_count(), in which case the capacity of the table is
+  // returned.  |index| must not be smaller than dropped_entry_count().
+  uint64_t MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const;
+
   // Change dynamic table capacity to |capacity|.  Returns true on success.
   // Returns false is |capacity| exceeds maximum dynamic table capacity.
   bool SetDynamicTableCapacity(uint64_t capacity);
diff --git a/quic/core/qpack/qpack_header_table_test.cc b/quic/core/qpack/qpack_header_table_test.cc
index 7d1044b..592d2cb 100644
--- a/quic/core/qpack/qpack_header_table_test.cc
+++ b/quic/core/qpack/qpack_header_table_test.cc
@@ -365,6 +365,55 @@
               /* expected_is_static = */ false, 2u);
 }
 
+// Returns the size of the largest entry that could be inserted into the
+// dynamic table without evicting entry |index|.
+TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) {
+  const uint64_t dynamic_table_capacity = 100;
+  QpackHeaderTable table;
+  table.SetMaximumDynamicTableCapacity(dynamic_table_capacity);
+
+  // Empty table can take an entry up to its capacity.
+  EXPECT_EQ(dynamic_table_capacity,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(0));
+
+  const uint64_t entry_size1 = QpackEntry::Size("foo", "bar");
+  EXPECT_TRUE(table.InsertEntry("foo", "bar"));
+  EXPECT_EQ(dynamic_table_capacity - entry_size1,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(0));
+  // Table can take an entry up to its capacity if all entries are allowed to be
+  // evicted.
+  EXPECT_EQ(dynamic_table_capacity,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(1));
+
+  const uint64_t entry_size2 = QpackEntry::Size("baz", "foobar");
+  EXPECT_TRUE(table.InsertEntry("baz", "foobar"));
+  // Table can take an entry up to its capacity if all entries are allowed to be
+  // evicted.
+  EXPECT_EQ(dynamic_table_capacity,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(2));
+  // Second entry must stay.
+  EXPECT_EQ(dynamic_table_capacity - entry_size2,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(1));
+  // First and second entry must stay.
+  EXPECT_EQ(dynamic_table_capacity - entry_size2 - entry_size1,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(0));
+
+  // Third entry evicts first one.
+  const uint64_t entry_size3 = QpackEntry::Size("last", "entry");
+  EXPECT_TRUE(table.InsertEntry("last", "entry"));
+  EXPECT_EQ(1u, table.dropped_entry_count());
+  // Table can take an entry up to its capacity if all entries are allowed to be
+  // evicted.
+  EXPECT_EQ(dynamic_table_capacity,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(3));
+  // Third entry must stay.
+  EXPECT_EQ(dynamic_table_capacity - entry_size3,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(2));
+  // Second and third entry must stay.
+  EXPECT_EQ(dynamic_table_capacity - entry_size3 - entry_size2,
+            table.MaxInsertSizeWithoutEvictingGivenEntry(1));
+}
+
 TEST_F(QpackHeaderTableTest, Observer) {
   StrictMock<MockObserver> observer1;
   RegisterObserver(&observer1, 1);