blob: f69e512ba82d35f1f657a4c2fa43ea1f7b8cb3bf [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2012 The Chromium Authors
[email protected]ba79eca2012-01-06 21:03:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef NET_BASE_PRIORITY_QUEUE_H_
6#define NET_BASE_PRIORITY_QUEUE_H_
[email protected]ba79eca2012-01-06 21:03:567
Avi Drissman13fc8932015-12-20 04:40:468#include <stddef.h>
9#include <stdint.h>
10
[email protected]ba79eca2012-01-06 21:03:5611#include <list>
David Bienvenua03ac8c2020-11-06 15:55:3912#include <utility>
[email protected]ba79eca2012-01-06 21:03:5613#include <vector>
14
Hans Wennborg0cb546b2020-06-23 18:00:0915#include "base/check_op.h"
Avi Drissman41c4a412023-01-11 22:45:3716#include "base/functional/bind.h"
17#include "base/functional/callback.h"
gab47aa7da2017-06-02 16:09:4318#include "base/threading/thread_checker.h"
[email protected]ba79eca2012-01-06 21:03:5619
20#if !defined(NDEBUG)
davidben1e912ea2016-04-20 19:17:0721#include <unordered_set>
[email protected]ba79eca2012-01-06 21:03:5622#endif
23
24namespace net {
25
26// A simple priority queue. The order of values is by priority and then FIFO.
27// Unlike the std::priority_queue, this implementation allows erasing elements
28// from the queue, and all operations are O(p) time for p priority levels.
29// The queue is agnostic to priority ordering (whether 0 precedes 1).
30// If the highest priority is 0, FirstMin() returns the first in order.
31//
32// In debug-mode, the internal queues store (id, value) pairs where id is used
33// to validate Pointers.
34//
gab47aa7da2017-06-02 16:09:4335template <typename T>
36class PriorityQueue {
[email protected]ba79eca2012-01-06 21:03:5637 private:
38 // This section is up-front for Pointer only.
39#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:5340 typedef std::list<std::pair<unsigned, T> > List;
[email protected]ba79eca2012-01-06 21:03:5641#else
42 typedef std::list<T> List;
43#endif
44
45 public:
wtc69f8ea82015-06-04 00:08:1346 typedef uint32_t Priority;
[email protected]ba79eca2012-01-06 21:03:5647
48 // A pointer to a value stored in the queue. The pointer becomes invalid
49 // when the queue is destroyed or cleared, or the value is erased.
50 class Pointer {
51 public:
52 // Constructs a null pointer.
[email protected]b3601bc22012-02-21 21:23:2053 Pointer() : priority_(kNullPriority) {
54#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:5355 id_ = static_cast<unsigned>(-1);
[email protected]b3601bc22012-02-21 21:23:2056#endif
[email protected]d826a97b2013-11-20 00:35:1457 // TODO(syzm)
58 // An uninitialized iterator behaves like an uninitialized pointer as per
59 // the STL docs. The fix below is ugly and should possibly be replaced
60 // with a better approach.
61 iterator_ = dummy_empty_list_.end();
[email protected]b3601bc22012-02-21 21:23:2062 }
[email protected]ba79eca2012-01-06 21:03:5663
[email protected]d826a97b2013-11-20 00:35:1464 Pointer(const Pointer& p)
65 : priority_(p.priority_),
66 iterator_(p.iterator_) {
[email protected]043324f42012-06-02 00:18:5367#if !defined(NDEBUG)
68 id_ = p.id_;
69#endif
70 }
71
72 Pointer& operator=(const Pointer& p) {
73 // Self-assignment is benign.
74 priority_ = p.priority_;
75 iterator_ = p.iterator_;
76#if !defined(NDEBUG)
77 id_ = p.id_;
78#endif
79 return *this;
80 }
81
[email protected]ba79eca2012-01-06 21:03:5682 bool is_null() const { return priority_ == kNullPriority; }
83
84 Priority priority() const { return priority_; }
85
86#if !defined(NDEBUG)
87 const T& value() const { return iterator_->second; }
88#else
89 const T& value() const { return *iterator_; }
90#endif
91
92 // Comparing to Pointer from a different PriorityQueue is undefined.
93 bool Equals(const Pointer& other) const {
94 return (priority_ == other.priority_) && (iterator_ == other.iterator_);
95 }
96
[email protected]b3601bc22012-02-21 21:23:2097 void Reset() {
98 *this = Pointer();
99 }
100
[email protected]ba79eca2012-01-06 21:03:56101 private:
102 friend class PriorityQueue;
103
[email protected]48118c32013-10-24 00:39:16104 // Note that we need iterator and not const_iterator to pass to
105 // List::erase. When C++11 is turned on for Chromium, this could
106 // be changed to const_iterator and the const_casts in the rest of
107 // the file can be removed.
[email protected]ba79eca2012-01-06 21:03:56108 typedef typename PriorityQueue::List::iterator ListIterator;
109
110 static const Priority kNullPriority = static_cast<Priority>(-1);
111
[email protected]48118c32013-10-24 00:39:16112 // It is guaranteed that Pointer will treat |iterator| as a
113 // const_iterator.
[email protected]ba79eca2012-01-06 21:03:56114 Pointer(Priority priority, const ListIterator& iterator)
115 : priority_(priority), iterator_(iterator) {
116#if !defined(NDEBUG)
117 id_ = iterator_->first;
118#endif
119 }
120
121 Priority priority_;
122 ListIterator iterator_;
[email protected]d826a97b2013-11-20 00:35:14123 // The STL iterators when uninitialized are like uninitialized pointers
124 // which cause crashes when assigned to other iterators. We need to
125 // initialize a NULL iterator to the end of a valid list.
126 List dummy_empty_list_;
[email protected]ba79eca2012-01-06 21:03:56127
128#if !defined(NDEBUG)
129 // Used by the queue to check if a Pointer is valid.
[email protected]043324f42012-06-02 00:18:53130 unsigned id_;
[email protected]ba79eca2012-01-06 21:03:56131#endif
132 };
133
134 // Creates a new queue for |num_priorities|.
Tsuyoshi Horo11e321eb2022-06-07 07:56:40135 explicit PriorityQueue(Priority num_priorities) : lists_(num_priorities) {
[email protected]ba79eca2012-01-06 21:03:56136#if !defined(NDEBUG)
137 next_id_ = 0;
138#endif
139 }
140
David Bienvenua03ac8c2020-11-06 15:55:39141 PriorityQueue(const PriorityQueue&) = delete;
142 PriorityQueue& operator=(const PriorityQueue&) = delete;
gab47aa7da2017-06-02 16:09:43143 ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
144
[email protected]ba79eca2012-01-06 21:03:56145 // Adds |value| with |priority| to the queue. Returns a pointer to the
146 // created element.
Lily Chen8ce89e42018-10-23 21:13:58147 Pointer Insert(T value, Priority priority) {
gab47aa7da2017-06-02 16:09:43148 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56149 DCHECK_LT(priority, lists_.size());
150 ++size_;
151 List& list = lists_[priority];
152#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:53153 unsigned id = next_id_;
[email protected]ba79eca2012-01-06 21:03:56154 valid_ids_.insert(id);
155 ++next_id_;
Lily Chen8ce89e42018-10-23 21:13:58156 list.emplace_back(std::make_pair(id, std::move(value)));
[email protected]ba79eca2012-01-06 21:03:56157#else
Lily Chen8ce89e42018-10-23 21:13:58158 list.emplace_back(std::move(value));
[email protected]ba79eca2012-01-06 21:03:56159#endif
Lily Chen8ce89e42018-10-23 21:13:58160 return Pointer(priority, std::prev(list.end()));
[email protected]ba79eca2012-01-06 21:03:56161 }
162
[email protected]daae1322013-09-05 18:26:50163 // Adds |value| with |priority| to the queue. Returns a pointer to the
164 // created element.
Lily Chen8ce89e42018-10-23 21:13:58165 Pointer InsertAtFront(T value, Priority priority) {
gab47aa7da2017-06-02 16:09:43166 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]daae1322013-09-05 18:26:50167 DCHECK_LT(priority, lists_.size());
168 ++size_;
169 List& list = lists_[priority];
170#if !defined(NDEBUG)
171 unsigned id = next_id_;
172 valid_ids_.insert(id);
173 ++next_id_;
Lily Chen8ce89e42018-10-23 21:13:58174 list.emplace_front(std::make_pair(id, std::move(value)));
[email protected]daae1322013-09-05 18:26:50175#else
Lily Chen8ce89e42018-10-23 21:13:58176 list.emplace_front(std::move(value));
[email protected]daae1322013-09-05 18:26:50177#endif
Lily Chen8ce89e42018-10-23 21:13:58178 return Pointer(priority, list.begin());
[email protected]daae1322013-09-05 18:26:50179 }
180
[email protected]ba79eca2012-01-06 21:03:56181 // Removes the value pointed by |pointer| from the queue. All pointers to this
Lily Chen8ce89e42018-10-23 21:13:58182 // value including |pointer| become invalid. Returns the erased value.
183 T Erase(const Pointer& pointer) {
gab47aa7da2017-06-02 16:09:43184 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56185 DCHECK_LT(pointer.priority_, lists_.size());
186 DCHECK_GT(size_, 0u);
187
188#if !defined(NDEBUG)
189 DCHECK_EQ(1u, valid_ids_.erase(pointer.id_));
190 DCHECK_EQ(pointer.iterator_->first, pointer.id_);
Lily Chen8ce89e42018-10-23 21:13:58191 T erased = std::move(pointer.iterator_->second);
192#else
193 T erased = std::move(*pointer.iterator_);
[email protected]ba79eca2012-01-06 21:03:56194#endif
195
196 --size_;
197 lists_[pointer.priority_].erase(pointer.iterator_);
Lily Chen8ce89e42018-10-23 21:13:58198 return erased;
[email protected]ba79eca2012-01-06 21:03:56199 }
200
201 // Returns a pointer to the first value of minimum priority or a null-pointer
202 // if empty.
[email protected]48118c32013-10-24 00:39:16203 Pointer FirstMin() const {
gab47aa7da2017-06-02 16:09:43204 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56205 for (size_t i = 0; i < lists_.size(); ++i) {
[email protected]48118c32013-10-24 00:39:16206 List* list = const_cast<List*>(&lists_[i]);
207 if (!list->empty())
208 return Pointer(i, list->begin());
[email protected]ba79eca2012-01-06 21:03:56209 }
210 return Pointer();
211 }
212
213 // Returns a pointer to the last value of minimum priority or a null-pointer
214 // if empty.
[email protected]48118c32013-10-24 00:39:16215 Pointer LastMin() const {
gab47aa7da2017-06-02 16:09:43216 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56217 for (size_t i = 0; i < lists_.size(); ++i) {
[email protected]48118c32013-10-24 00:39:16218 List* list = const_cast<List*>(&lists_[i]);
219 if (!list->empty())
220 return Pointer(i, --list->end());
[email protected]ba79eca2012-01-06 21:03:56221 }
222 return Pointer();
223 }
224
225 // Returns a pointer to the first value of maximum priority or a null-pointer
226 // if empty.
[email protected]48118c32013-10-24 00:39:16227 Pointer FirstMax() const {
gab47aa7da2017-06-02 16:09:43228 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56229 for (size_t i = lists_.size(); i > 0; --i) {
230 size_t index = i - 1;
[email protected]48118c32013-10-24 00:39:16231 List* list = const_cast<List*>(&lists_[index]);
232 if (!list->empty())
233 return Pointer(index, list->begin());
[email protected]ba79eca2012-01-06 21:03:56234 }
235 return Pointer();
236 }
237
238 // Returns a pointer to the last value of maximum priority or a null-pointer
239 // if empty.
[email protected]48118c32013-10-24 00:39:16240 Pointer LastMax() const {
gab47aa7da2017-06-02 16:09:43241 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56242 for (size_t i = lists_.size(); i > 0; --i) {
243 size_t index = i - 1;
[email protected]48118c32013-10-24 00:39:16244 List* list = const_cast<List*>(&lists_[index]);
245 if (!list->empty())
246 return Pointer(index, --list->end());
[email protected]ba79eca2012-01-06 21:03:56247 }
248 return Pointer();
249 }
250
Lily Chen8ce89e42018-10-23 21:13:58251 // Given an ordering of the values in this queue by decreasing priority and
252 // then FIFO, returns a pointer to the value following the value of the given
253 // pointer (which must be non-NULL). I.e., gets the next element in decreasing
254 // priority, then FIFO order. If the given pointer is already pointing at the
255 // last value, returns a null Pointer.
[email protected]48118c32013-10-24 00:39:16256 //
Lily Chen8ce89e42018-10-23 21:13:58257 // (One could also implement GetNextTowardsFirstMin() [decreasing priority,
258 // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing
259 // priority, then {,reverse} FIFO].)
[email protected]48118c32013-10-24 00:39:16260 Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
gab47aa7da2017-06-02 16:09:43261 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16262 DCHECK(!pointer.is_null());
263 DCHECK_LT(pointer.priority_, lists_.size());
264
265 typename Pointer::ListIterator it = pointer.iterator_;
266 Priority priority = pointer.priority_;
267 DCHECK(it != lists_[priority].end());
268 ++it;
269 while (it == lists_[priority].end()) {
Lily Chen8ce89e42018-10-23 21:13:58270 if (priority == 0u) {
271 DCHECK(pointer.Equals(LastMin()));
[email protected]48118c32013-10-24 00:39:16272 return Pointer();
Lily Chen8ce89e42018-10-23 21:13:58273 }
[email protected]48118c32013-10-24 00:39:16274 --priority;
275 it = const_cast<List*>(&lists_[priority])->begin();
276 }
277 return Pointer(priority, it);
278 }
279
Lily Chen8ce89e42018-10-23 21:13:58280 // Given an ordering of the values in this queue by decreasing priority and
281 // then FIFO, returns a pointer to the value preceding the value of the given
282 // pointer (which must be non-NULL). I.e., gets the next element in increasing
283 // priority, then reverse FIFO order. If the given pointer is already pointing
284 // at the first value, returns a null Pointer.
285 Pointer GetPreviousTowardsFirstMax(const Pointer& pointer) const {
286 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
287 DCHECK(!pointer.is_null());
288 DCHECK_LT(pointer.priority_, lists_.size());
289
290 typename Pointer::ListIterator it = pointer.iterator_;
291 Priority priority = pointer.priority_;
292 DCHECK(it != lists_[priority].end());
293 while (it == lists_[priority].begin()) {
294 if (priority == num_priorities() - 1) {
295 DCHECK(pointer.Equals(FirstMax()));
296 return Pointer();
297 }
298 ++priority;
299 it = const_cast<List*>(&lists_[priority])->end();
300 }
301 return Pointer(priority, std::prev(it));
302 }
303
304 // Checks whether |lhs| is closer in the queue to the first max element than
305 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
306 bool IsCloserToFirstMaxThan(const Pointer& lhs, const Pointer& rhs) {
307 if (lhs.Equals(rhs))
308 return false;
309 if (lhs.priority_ == rhs.priority_) {
310 // Traverse list starting from lhs and see if we find rhs.
311 for (auto it = lhs.iterator_; it != lists_[lhs.priority_].end(); ++it) {
312 if (it == rhs.iterator_)
313 return true;
314 }
315 return false;
316 }
317 return lhs.priority_ > rhs.priority_;
318 }
319
320 // Checks whether |lhs| is closer in the queue to the last min element than
321 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
322 bool IsCloserToLastMinThan(const Pointer& lhs, const Pointer& rhs) {
323 return !lhs.Equals(rhs) && !IsCloserToFirstMaxThan(lhs, rhs);
324 }
325
326 // Finds the first element (with respect to decreasing priority, then FIFO
327 // order) which matches the given predicate.
328 Pointer FindIf(const base::RepeatingCallback<bool(T)>& pred) {
329 for (auto pointer = FirstMax(); !pointer.is_null();
330 pointer = GetNextTowardsLastMin(pointer)) {
331 if (pred.Run(pointer.value()))
332 return pointer;
333 }
334 return Pointer();
335 }
336
[email protected]ba79eca2012-01-06 21:03:56337 // Empties the queue. All pointers become invalid.
338 void Clear() {
gab47aa7da2017-06-02 16:09:43339 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56340 for (size_t i = 0; i < lists_.size(); ++i) {
341 lists_[i].clear();
342 }
343#if !defined(NDEBUG)
344 valid_ids_.clear();
345#endif
346 size_ = 0u;
347 }
348
[email protected]daae1322013-09-05 18:26:50349 // Returns the number of priorities the queue supports.
[email protected]48118c32013-10-24 00:39:16350 size_t num_priorities() const {
gab47aa7da2017-06-02 16:09:43351 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16352 return lists_.size();
353 }
354
355 bool empty() const {
gab47aa7da2017-06-02 16:09:43356 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16357 return size_ == 0;
358 }
[email protected]daae1322013-09-05 18:26:50359
[email protected]ba79eca2012-01-06 21:03:56360 // Returns number of queued values.
361 size_t size() const {
gab47aa7da2017-06-02 16:09:43362 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56363 return size_;
364 }
365
366 private:
367 typedef std::vector<List> ListVector;
368
369#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:53370 unsigned next_id_;
davidben1e912ea2016-04-20 19:17:07371 std::unordered_set<unsigned> valid_ids_;
[email protected]ba79eca2012-01-06 21:03:56372#endif
373
374 ListVector lists_;
Tsuyoshi Horo11e321eb2022-06-07 07:56:40375 size_t size_ = 0;
[email protected]043324f42012-06-02 00:18:53376
gab47aa7da2017-06-02 16:09:43377 THREAD_CHECKER(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56378};
379
380} // namespace net
381
382#endif // NET_BASE_PRIORITY_QUEUE_H_