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