xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/quiche_linked_hash_map.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This is a simplistic insertion-ordered map.  It behaves similarly to an STL
6 // map, but only implements a small subset of the map's methods.  Internally, we
7 // just keep a map and a list going in parallel.
8 //
9 // This class provides no thread safety guarantees, beyond what you would
10 // normally see with std::list.
11 //
12 // Iterators point into the list and should be stable in the face of
13 // mutations, except for an iterator pointing to an element that was just
14 // deleted.
15 
16 #ifndef QUICHE_COMMON_QUICHE_LINKED_HASH_MAP_H_
17 #define QUICHE_COMMON_QUICHE_LINKED_HASH_MAP_H_
18 
19 #include <functional>
20 #include <list>
21 #include <tuple>
22 #include <type_traits>
23 #include <utility>
24 
25 #include "absl/container/flat_hash_map.h"
26 #include "absl/hash/hash.h"
27 #include "quiche/common/platform/api/quiche_export.h"
28 #include "quiche/common/platform/api/quiche_logging.h"
29 
30 namespace quiche {
31 
32 // This holds a list of pair<Key, Value> items.  This list is what gets
33 // traversed, and it's iterators from this list that we return from
34 // begin/end/find.
35 //
36 // We also keep a set<list::iterator> for find.  Since std::list is a
37 // doubly-linked list, the iterators should remain stable.
38 
39 // QUICHE_NO_EXPORT comments suppress erroneous presubmit failures.
40 template <class Key,                      // QUICHE_NO_EXPORT
41           class Value,                    // QUICHE_NO_EXPORT
42           class Hash = absl::Hash<Key>,   // QUICHE_NO_EXPORT
43           class Eq = std::equal_to<Key>>  // QUICHE_NO_EXPORT
44 class QuicheLinkedHashMap {               // QUICHE_NO_EXPORT
45  private:
46   typedef std::list<std::pair<Key, Value>> ListType;
47   typedef absl::flat_hash_map<Key, typename ListType::iterator, Hash, Eq>
48       MapType;
49 
50  public:
51   typedef typename ListType::iterator iterator;
52   typedef typename ListType::reverse_iterator reverse_iterator;
53   typedef typename ListType::const_iterator const_iterator;
54   typedef typename ListType::const_reverse_iterator const_reverse_iterator;
55   typedef typename MapType::key_type key_type;
56   typedef typename ListType::value_type value_type;
57   typedef typename ListType::size_type size_type;
58 
59   QuicheLinkedHashMap() = default;
QuicheLinkedHashMap(size_type bucket_count)60   explicit QuicheLinkedHashMap(size_type bucket_count) : map_(bucket_count) {}
61 
62   QuicheLinkedHashMap(const QuicheLinkedHashMap& other) = delete;
63   QuicheLinkedHashMap& operator=(const QuicheLinkedHashMap& other) = delete;
64   QuicheLinkedHashMap(QuicheLinkedHashMap&& other) = default;
65   QuicheLinkedHashMap& operator=(QuicheLinkedHashMap&& other) = default;
66 
67   // Returns an iterator to the first (insertion-ordered) element.  Like a map,
68   // this can be dereferenced to a pair<Key, Value>.
begin()69   iterator begin() { return list_.begin(); }
begin()70   const_iterator begin() const { return list_.begin(); }
71 
72   // Returns an iterator beyond the last element.
end()73   iterator end() { return list_.end(); }
end()74   const_iterator end() const { return list_.end(); }
75 
76   // Returns an iterator to the last (insertion-ordered) element.  Like a map,
77   // this can be dereferenced to a pair<Key, Value>.
rbegin()78   reverse_iterator rbegin() { return list_.rbegin(); }
rbegin()79   const_reverse_iterator rbegin() const { return list_.rbegin(); }
80 
81   // Returns an iterator beyond the first element.
rend()82   reverse_iterator rend() { return list_.rend(); }
rend()83   const_reverse_iterator rend() const { return list_.rend(); }
84 
85   // Front and back accessors common to many stl containers.
86 
87   // Returns the earliest-inserted element
front()88   const value_type& front() const { return list_.front(); }
89 
90   // Returns the earliest-inserted element.
front()91   value_type& front() { return list_.front(); }
92 
93   // Returns the most-recently-inserted element.
back()94   const value_type& back() const { return list_.back(); }
95 
96   // Returns the most-recently-inserted element.
back()97   value_type& back() { return list_.back(); }
98 
99   // Clears the map of all values.
clear()100   void clear() {
101     map_.clear();
102     list_.clear();
103   }
104 
105   // Returns true iff the map is empty.
empty()106   bool empty() const { return list_.empty(); }
107 
108   // Removes the first element from the list.
pop_front()109   void pop_front() { erase(begin()); }
110 
111   // Erases values with the provided key.  Returns the number of elements
112   // erased.  In this implementation, this will be 0 or 1.
erase(const Key & key)113   size_type erase(const Key& key) {
114     typename MapType::iterator found = map_.find(key);
115     if (found == map_.end()) {
116       return 0;
117     }
118 
119     list_.erase(found->second);
120     map_.erase(found);
121 
122     return 1;
123   }
124 
125   // Erases the item that 'position' points to. Returns an iterator that points
126   // to the item that comes immediately after the deleted item in the list, or
127   // end().
128   // If the provided iterator is invalid or there is inconsistency between the
129   // map and list, a QUICHE_CHECK() error will occur.
erase(iterator position)130   iterator erase(iterator position) {
131     typename MapType::iterator found = map_.find(position->first);
132     QUICHE_CHECK(found->second == position)
133         << "Inconsistent iterator for map and list, or the iterator is "
134            "invalid.";
135 
136     map_.erase(found);
137     return list_.erase(position);
138   }
139 
140   // Erases all the items in the range [first, last).  Returns an iterator that
141   // points to the item that comes immediately after the last deleted item in
142   // the list, or end().
erase(iterator first,iterator last)143   iterator erase(iterator first, iterator last) {
144     while (first != last && first != end()) {
145       first = erase(first);
146     }
147     return first;
148   }
149 
150   // Finds the element with the given key.  Returns an iterator to the
151   // value found, or to end() if the value was not found.  Like a map, this
152   // iterator points to a pair<Key, Value>.
find(const Key & key)153   iterator find(const Key& key) {
154     typename MapType::iterator found = map_.find(key);
155     if (found == map_.end()) {
156       return end();
157     }
158     return found->second;
159   }
160 
find(const Key & key)161   const_iterator find(const Key& key) const {
162     typename MapType::const_iterator found = map_.find(key);
163     if (found == map_.end()) {
164       return end();
165     }
166     return found->second;
167   }
168 
contains(const Key & key)169   bool contains(const Key& key) const { return find(key) != end(); }
170 
171   // Returns the value mapped to key, or an inserted iterator to that position
172   // in the map.
173   Value& operator[](const key_type& key) {
174     return (*((this->insert(std::make_pair(key, Value()))).first)).second;
175   }
176 
177   // Inserts an element into the map
insert(const std::pair<Key,Value> & pair)178   std::pair<iterator, bool> insert(const std::pair<Key, Value>& pair) {
179     return InsertInternal(pair);
180   }
181 
182   // Inserts an element into the map
insert(std::pair<Key,Value> && pair)183   std::pair<iterator, bool> insert(std::pair<Key, Value>&& pair) {
184     return InsertInternal(std::move(pair));
185   }
186 
187   // Derive size_ from map_, as list::size might be O(N).
size()188   size_type size() const { return map_.size(); }
189 
190   template <typename... Args>
emplace(Args &&...args)191   std::pair<iterator, bool> emplace(Args&&... args) {
192     ListType node_donor;
193     auto node_pos =
194         node_donor.emplace(node_donor.end(), std::forward<Args>(args)...);
195     const auto& k = node_pos->first;
196     auto ins = map_.insert({k, node_pos});
197     if (!ins.second) {
198       return {ins.first->second, false};
199     }
200     list_.splice(list_.end(), node_donor, node_pos);
201     return {ins.first->second, true};
202   }
203 
swap(QuicheLinkedHashMap & other)204   void swap(QuicheLinkedHashMap& other) {
205     map_.swap(other.map_);
206     list_.swap(other.list_);
207   }
208 
209  private:
210   template <typename U>
InsertInternal(U && pair)211   std::pair<iterator, bool> InsertInternal(U&& pair) {
212     auto insert_result = map_.try_emplace(pair.first);
213     auto map_iter = insert_result.first;
214 
215     // If the map already contains this key, return a pair with an iterator to
216     // it, and false indicating that we didn't insert anything.
217     if (!insert_result.second) {
218       return {map_iter->second, false};
219     }
220 
221     // Otherwise, insert into the list, and set value in map.
222     auto list_iter = list_.insert(list_.end(), std::forward<U>(pair));
223     map_iter->second = list_iter;
224 
225     return {list_iter, true};
226   }
227 
228   // The map component, used for speedy lookups
229   MapType map_;
230 
231   // The list component, used for maintaining insertion order
232   ListType list_;
233 };
234 
235 }  // namespace quiche
236 
237 #endif  // QUICHE_COMMON_QUICHE_LINKED_HASH_MAP_H_
238