Use absl::flat_hash_map instead of std::unordered_map in PriorityWriteScheduler.

PiperOrigin-RevId: 448044819
diff --git a/quiche/http2/core/priority_write_scheduler.h b/quiche/http2/core/priority_write_scheduler.h
index 2466fbf..c6d06b2 100644
--- a/quiche/http2/core/priority_write_scheduler.h
+++ b/quiche/http2/core/priority_write_scheduler.h
@@ -8,12 +8,13 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
+#include <memory>
 #include <string>
 #include <tuple>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
 #include "absl/strings/str_cat.h"
 #include "quiche/http2/core/write_scheduler.h"
 #include "quiche/common/platform/api/quiche_bug_tracker.h"
@@ -66,9 +67,11 @@
           << "Stream " << root_stream_id_ << " already registered";
       return;
     }
-    StreamInfo stream_info = {precedence.spdy3_priority(), stream_id, false};
+    auto stream_info = std::make_unique<StreamInfo>(
+        StreamInfo{precedence.spdy3_priority(), stream_id, false});
     bool inserted =
-        stream_infos_.insert(std::make_pair(stream_id, stream_info)).second;
+        stream_infos_.insert(std::make_pair(stream_id, std::move(stream_info)))
+            .second;
     QUICHE_BUG_IF(spdy_bug_19_2, !inserted)
         << "Stream " << stream_id << " already registered";
   }
@@ -79,10 +82,10 @@
       QUICHE_BUG(spdy_bug_19_3) << "Stream " << stream_id << " not registered";
       return;
     }
-    StreamInfo& stream_info = it->second;
-    if (stream_info.ready) {
-      bool erased =
-          Erase(&priority_infos_[stream_info.priority].ready_list, stream_info);
+    const StreamInfo* const stream_info = it->second.get();
+    if (stream_info->ready) {
+      bool erased = Erase(&priority_infos_[stream_info->priority].ready_list,
+                          stream_info);
       QUICHE_DCHECK(erased);
     }
     stream_infos_.erase(it);
@@ -99,7 +102,7 @@
       QUICHE_DVLOG(1) << "Stream " << stream_id << " not registered";
       return StreamPrecedenceType(spdy::kV3LowestPriority);
     }
-    return StreamPrecedenceType(it->second.priority);
+    return StreamPrecedenceType(it->second->priority);
   }
 
   void UpdateStreamPrecedence(StreamIdType stream_id,
@@ -120,19 +123,19 @@
       QUICHE_DVLOG(1) << "Stream " << stream_id << " not registered";
       return;
     }
-    StreamInfo& stream_info = it->second;
+    StreamInfo* const stream_info = it->second.get();
     spdy::SpdyPriority new_priority = precedence.spdy3_priority();
-    if (stream_info.priority == new_priority) {
+    if (stream_info->priority == new_priority) {
       return;
     }
-    if (stream_info.ready) {
-      bool erased =
-          Erase(&priority_infos_[stream_info.priority].ready_list, stream_info);
+    if (stream_info->ready) {
+      bool erased = Erase(&priority_infos_[stream_info->priority].ready_list,
+                          stream_info);
       QUICHE_DCHECK(erased);
-      priority_infos_[new_priority].ready_list.push_back(&stream_info);
+      priority_infos_[new_priority].ready_list.push_back(stream_info);
       ++num_ready_streams_;
     }
-    stream_info.priority = new_priority;
+    stream_info->priority = new_priority;
   }
 
   std::vector<StreamIdType> GetStreamChildren(
@@ -147,7 +150,7 @@
       QUICHE_BUG(spdy_bug_19_4) << "Stream " << stream_id << " not registered";
       return;
     }
-    PriorityInfo& priority_info = priority_infos_[it->second.priority];
+    PriorityInfo& priority_info = priority_infos_[it->second->priority];
     priority_info.last_event_time_usec =
         std::max(priority_info.last_event_time_usec, now_in_usec);
   }
@@ -159,9 +162,9 @@
       return 0;
     }
     int64_t last_event_time_usec = 0;
-    const StreamInfo& stream_info = it->second;
+    const StreamInfo* const stream_info = it->second.get();
     for (spdy::SpdyPriority p = spdy::kV3HighestPriority;
-         p < stream_info.priority; ++p) {
+         p < stream_info->priority; ++p) {
       last_event_time_usec = std::max(last_event_time_usec,
                                       priority_infos_[p].last_event_time_usec);
     }
@@ -179,7 +182,7 @@
          p <= spdy::kV3LowestPriority; ++p) {
       ReadyList& ready_list = priority_infos_[p].ready_list;
       if (!ready_list.empty()) {
-        StreamInfo* info = ready_list.front();
+        StreamInfo* const info = ready_list.front();
         ready_list.pop_front();
         --num_ready_streams_;
 
@@ -202,9 +205,9 @@
     }
 
     // If there's a higher priority stream, this stream should yield.
-    const StreamInfo& stream_info = it->second;
+    const StreamInfo* const stream_info = it->second.get();
     for (spdy::SpdyPriority p = spdy::kV3HighestPriority;
-         p < stream_info.priority; ++p) {
+         p < stream_info->priority; ++p) {
       if (!priority_infos_[p].ready_list.empty()) {
         return true;
       }
@@ -212,7 +215,7 @@
 
     // If this priority level is empty, or this stream is the next up, there's
     // no need to yield.
-    const auto& ready_list = priority_infos_[it->second.priority].ready_list;
+    const auto& ready_list = priority_infos_[it->second->priority].ready_list;
     if (ready_list.empty() || ready_list.front()->stream_id == stream_id) {
       return false;
     }
@@ -228,18 +231,18 @@
       QUICHE_BUG(spdy_bug_19_8) << "Stream " << stream_id << " not registered";
       return;
     }
-    StreamInfo& stream_info = it->second;
-    if (stream_info.ready) {
+    StreamInfo* const stream_info = it->second.get();
+    if (stream_info->ready) {
       return;
     }
-    ReadyList& ready_list = priority_infos_[stream_info.priority].ready_list;
+    ReadyList& ready_list = priority_infos_[stream_info->priority].ready_list;
     if (add_to_front) {
-      ready_list.push_front(&stream_info);
+      ready_list.push_front(stream_info);
     } else {
-      ready_list.push_back(&stream_info);
+      ready_list.push_back(stream_info);
     }
     ++num_ready_streams_;
-    stream_info.ready = true;
+    stream_info->ready = true;
   }
 
   void MarkStreamNotReady(StreamIdType stream_id) override {
@@ -248,14 +251,14 @@
       QUICHE_BUG(spdy_bug_19_9) << "Stream " << stream_id << " not registered";
       return;
     }
-    StreamInfo& stream_info = it->second;
-    if (!stream_info.ready) {
+    StreamInfo* const stream_info = it->second.get();
+    if (!stream_info->ready) {
       return;
     }
     bool erased =
-        Erase(&priority_infos_[stream_info.priority].ready_list, stream_info);
+        Erase(&priority_infos_[stream_info->priority].ready_list, stream_info);
     QUICHE_DCHECK(erased);
-    stream_info.ready = false;
+    stream_info->ready = false;
   }
 
   // Returns true iff the number of ready streams is non-zero.
@@ -279,7 +282,7 @@
       QUICHE_DLOG(INFO) << "Stream " << stream_id << " not registered";
       return false;
     }
-    return it->second.ready;
+    return it->second->ready;
   }
 
  private:
@@ -304,12 +307,15 @@
     int64_t last_event_time_usec = 0;
   };
 
-  typedef std::unordered_map<StreamIdType, StreamInfo> StreamInfoMap;
+  // Use std::unique_ptr, because absl::flat_hash_map does not have pointer
+  // stability, but ReadyList stores pointers to the StreamInfo objects.
+  using StreamInfoMap =
+      absl::flat_hash_map<StreamIdType, std::unique_ptr<StreamInfo>>;
 
   // Erases `info` from `ready_list`, returning true if found (and erased), or
   // false otherwise. Decrements `num_ready_streams_` if an entry is erased.
-  bool Erase(ReadyList* ready_list, const StreamInfo& info) {
-    auto it = std::remove(ready_list->begin(), ready_list->end(), &info);
+  bool Erase(ReadyList* ready_list, const StreamInfo* info) {
+    auto it = std::remove(ready_list->begin(), ready_list->end(), info);
     if (it == ready_list->end()) {
       // `info` was not found.
       return false;