Add QpackBlockingManager to track blocked streams and blocking entries.

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 260829566
Change-Id: I3f2f74384f213f3dfea8460a77f759b070a7d79c
diff --git a/quic/core/qpack/qpack_blocking_manager.cc b/quic/core/qpack/qpack_blocking_manager.cc
new file mode 100644
index 0000000..e8e4f87
--- /dev/null
+++ b/quic/core/qpack/qpack_blocking_manager.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h"
+
+#include <utility>
+
+namespace quic {
+
+QpackBlockingManager::QpackBlockingManager() : known_received_count_(0) {}
+
+bool QpackBlockingManager::OnHeaderAcknowledgement(QuicStreamId stream_id) {
+  auto it = header_blocks_.find(stream_id);
+  if (it == header_blocks_.end()) {
+    return false;
+  }
+
+  DCHECK(!it->second.empty());
+
+  const IndexSet& indices = it->second.front();
+  DCHECK(!indices.empty());
+
+  const uint64_t required_index_count = RequiredInsertCount(indices);
+  if (known_received_count_ < required_index_count) {
+    known_received_count_ = required_index_count;
+  }
+
+  DecreaseReferenceCounts(indices);
+
+  it->second.pop_front();
+  if (it->second.empty()) {
+    header_blocks_.erase(it);
+  }
+
+  return true;
+}
+
+void QpackBlockingManager::OnStreamCancellation(QuicStreamId stream_id) {
+  auto it = header_blocks_.find(stream_id);
+  if (it == header_blocks_.end()) {
+    return;
+  }
+
+  for (const IndexSet& indices : it->second) {
+    DecreaseReferenceCounts(indices);
+  }
+
+  header_blocks_.erase(it);
+}
+
+void QpackBlockingManager::OnInsertCountIncrement(uint64_t increment) {
+  known_received_count_ += increment;
+}
+
+void QpackBlockingManager::OnHeaderBlockSent(QuicStreamId stream_id,
+                                             IndexSet indices) {
+  DCHECK(!indices.empty());
+
+  IncreaseReferenceCounts(indices);
+  header_blocks_[stream_id].push_back(std::move(indices));
+}
+
+uint64_t QpackBlockingManager::blocked_stream_count() const {
+  uint64_t blocked_stream_count = 0;
+  for (const auto& header_blocks_for_stream : header_blocks_) {
+    for (const IndexSet& indices : header_blocks_for_stream.second) {
+      if (RequiredInsertCount(indices) > known_received_count_) {
+        ++blocked_stream_count;
+        break;
+      }
+    }
+  }
+
+  return blocked_stream_count;
+}
+
+uint64_t QpackBlockingManager::smallest_blocking_index() const {
+  return entry_reference_counts_.empty()
+             ? std::numeric_limits<uint64_t>::max()
+             : entry_reference_counts_.begin()->first;
+}
+
+// static
+uint64_t QpackBlockingManager::RequiredInsertCount(const IndexSet& indices) {
+  return *indices.rbegin() + 1;
+}
+
+void QpackBlockingManager::IncreaseReferenceCounts(const IndexSet& indices) {
+  for (const uint64_t index : indices) {
+    auto it = entry_reference_counts_.lower_bound(index);
+    if (it != entry_reference_counts_.end() && it->first == index) {
+      ++it->second;
+    } else {
+      entry_reference_counts_.insert(it, {index, 1});
+    }
+  }
+}
+
+void QpackBlockingManager::DecreaseReferenceCounts(const IndexSet& indices) {
+  for (const uint64_t index : indices) {
+    auto it = entry_reference_counts_.find(index);
+    DCHECK(it != entry_reference_counts_.end());
+    DCHECK_NE(0u, it->second);
+
+    if (it->second == 1) {
+      entry_reference_counts_.erase(it);
+    } else {
+      --it->second;
+    }
+  }
+}
+
+}  // namespace quic
diff --git a/quic/core/qpack/qpack_blocking_manager.h b/quic/core/qpack/qpack_blocking_manager.h
new file mode 100644
index 0000000..3834d92
--- /dev/null
+++ b/quic/core/qpack/qpack_blocking_manager.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_
+
+#include <cstdint>
+#include <map>
+#include <set>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Class to keep track of blocked streams and blocking dynamic table entries:
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-decoding
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-insertion
+class QUIC_EXPORT_PRIVATE QpackBlockingManager {
+ public:
+  using IndexSet = std::multiset<uint64_t>;
+
+  QpackBlockingManager();
+
+  // Called when a Header Acknowledgement instruction is received on the decoder
+  // stream.  Returns false if there are no outstanding header blocks to be
+  // acknowledged on |stream_id|.
+  bool OnHeaderAcknowledgement(QuicStreamId stream_id);
+
+  // Called when a Stream Cancellation instruction is received on the decoder
+  // stream.
+  void OnStreamCancellation(QuicStreamId stream_id);
+
+  // Called when an Insert Count Increment instruction is received on the
+  // decoder stream.
+  void OnInsertCountIncrement(uint64_t increment);
+
+  // Called when sending a header block containing references to dynamic table
+  // entries with |indices|.  |indices| must not be empty.
+  void OnHeaderBlockSent(QuicStreamId stream_id, IndexSet indices);
+
+  // Returns the number of blocked streams.
+  uint64_t blocked_stream_count() const;
+
+  // Returns the index of the blocking entry with the smallest index,
+  // or std::numeric_limits<uint64_t>::max() if there are no blocking entries.
+  uint64_t smallest_blocking_index() const;
+
+  // Returns the Known Received Count as defined at
+  // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#known-received-count.
+  uint64_t known_received_count() const { return known_received_count_; }
+
+  // Required Insert Count for set of indices.
+  static uint64_t RequiredInsertCount(const IndexSet& indices);
+
+ private:
+  using HeaderBlocksForStream = QuicDeque<IndexSet>;
+  using HeaderBlocks = QuicUnorderedMap<QuicStreamId, HeaderBlocksForStream>;
+
+  // Increase or decrease the reference count for each index in |indices|.
+  void IncreaseReferenceCounts(const IndexSet& indices);
+  void DecreaseReferenceCounts(const IndexSet& indices);
+
+  // Multiset of indices in each header block for each stream.
+  // Must not contain a stream id with an empty queue.
+  HeaderBlocks header_blocks_;
+
+  // Number of references in |header_blocks_| for each entry index.
+  std::map<uint64_t, uint64_t> entry_reference_counts_;
+
+  uint64_t known_received_count_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QPACK_QPACK_BLOCKING_MANAGER_H_
diff --git a/quic/core/qpack/qpack_blocking_manager_test.cc b/quic/core/qpack/qpack_blocking_manager_test.cc
new file mode 100644
index 0000000..a7cba73
--- /dev/null
+++ b/quic/core/qpack/qpack_blocking_manager_test.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackBlockingManagerTest : public QuicTest {
+ protected:
+  QpackBlockingManagerTest() = default;
+  ~QpackBlockingManagerTest() override = default;
+
+  QpackBlockingManager manager_;
+};
+
+TEST_F(QpackBlockingManagerTest, Empty) {
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_EQ(0u, manager_.known_received_count());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+
+  EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_FALSE(manager_.OnHeaderAcknowledgement(1));
+}
+
+TEST_F(QpackBlockingManagerTest, NotBlockedByInsertCountIncrement) {
+  manager_.OnInsertCountIncrement(2);
+
+  // Stream 0 is not blocked, because it only references entries that are
+  // already acknowledged by an Insert Count Increment instruction.
+  manager_.OnHeaderBlockSent(0, {1, 0});
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+}
+
+TEST_F(QpackBlockingManagerTest, UnblockedByInsertCountIncrement) {
+  manager_.OnHeaderBlockSent(0, {1, 0});
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+
+  manager_.OnInsertCountIncrement(2);
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+}
+
+TEST_F(QpackBlockingManagerTest, NotBlockedByHeaderAcknowledgement) {
+  manager_.OnHeaderBlockSent(0, {2, 1, 1});
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+
+  // Stream 1 is not blocked, because it only references entries that are
+  // already acknowledged by a Header Acknowledgement instruction.
+  manager_.OnHeaderBlockSent(1, {2, 2});
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+}
+
+TEST_F(QpackBlockingManagerTest, UnblockedByHeaderAcknowledgement) {
+  manager_.OnHeaderBlockSent(0, {2, 1, 1});
+  manager_.OnHeaderBlockSent(1, {2, 2});
+  EXPECT_EQ(2u, manager_.blocked_stream_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+}
+
+TEST_F(QpackBlockingManagerTest, KnownReceivedCount) {
+  EXPECT_EQ(0u, manager_.known_received_count());
+
+  // Sending a header block does not change Known Received Count.
+  manager_.OnHeaderBlockSent(0, {0});
+  EXPECT_EQ(0u, manager_.known_received_count());
+
+  manager_.OnHeaderBlockSent(1, {1});
+  EXPECT_EQ(0u, manager_.known_received_count());
+
+  // Header Acknowledgement might increase Known Received Count.
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(1u, manager_.known_received_count());
+
+  manager_.OnHeaderBlockSent(2, {5});
+  EXPECT_EQ(1u, manager_.known_received_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
+  EXPECT_EQ(2u, manager_.known_received_count());
+
+  // Insert Count Increment increases Known Received Count.
+  manager_.OnInsertCountIncrement(2);
+  EXPECT_EQ(4u, manager_.known_received_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(2));
+  EXPECT_EQ(6u, manager_.known_received_count());
+
+  // Stream Cancellation does not change Known Received Count.
+  manager_.OnStreamCancellation(0);
+  EXPECT_EQ(6u, manager_.known_received_count());
+
+  // Header Acknowledgement of a block with smaller Required Insert Count does
+  // not increase Known Received Count.
+  manager_.OnHeaderBlockSent(0, {3});
+  EXPECT_EQ(6u, manager_.known_received_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(6u, manager_.known_received_count());
+
+  // Header Acknowledgement of a block with equal Required Insert Count does not
+  // increase Known Received Count.
+  manager_.OnHeaderBlockSent(1, {5});
+  EXPECT_EQ(6u, manager_.known_received_count());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
+  EXPECT_EQ(6u, manager_.known_received_count());
+}
+
+TEST_F(QpackBlockingManagerTest, SmallestBlockingIndex) {
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(0, {0});
+  EXPECT_EQ(0u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(1, {2});
+  EXPECT_EQ(0u, manager_.smallest_blocking_index());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(2u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(1, {1});
+  EXPECT_EQ(1u, manager_.smallest_blocking_index());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
+  EXPECT_EQ(1u, manager_.smallest_blocking_index());
+
+  // Insert Count Increment does not change smallest blocking index.
+  manager_.OnInsertCountIncrement(2);
+  EXPECT_EQ(1u, manager_.smallest_blocking_index());
+
+  manager_.OnStreamCancellation(1);
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+}
+
+TEST_F(QpackBlockingManagerTest, HeaderAcknowledgementsOnSingleStream) {
+  EXPECT_EQ(0u, manager_.known_received_count());
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(0, {2, 1, 1});
+  EXPECT_EQ(0u, manager_.known_received_count());
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(1u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(0, {1, 0});
+  EXPECT_EQ(0u, manager_.known_received_count());
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(0u, manager_.smallest_blocking_index());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(3u, manager_.known_received_count());
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_EQ(0u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(0, {3});
+  EXPECT_EQ(3u, manager_.known_received_count());
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(0u, manager_.smallest_blocking_index());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(3u, manager_.known_received_count());
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(3u, manager_.smallest_blocking_index());
+
+  EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
+  EXPECT_EQ(4u, manager_.known_received_count());
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+
+  EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0));
+}
+
+TEST_F(QpackBlockingManagerTest, CancelStream) {
+  manager_.OnHeaderBlockSent(0, {3});
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(3u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(0, {2});
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(2u, manager_.smallest_blocking_index());
+
+  manager_.OnHeaderBlockSent(1, {4});
+  EXPECT_EQ(2u, manager_.blocked_stream_count());
+  EXPECT_EQ(2u, manager_.smallest_blocking_index());
+
+  manager_.OnStreamCancellation(0);
+  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_EQ(4u, manager_.smallest_blocking_index());
+
+  manager_.OnStreamCancellation(1);
+  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            manager_.smallest_blocking_index());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic