blob: c4c30fa45c0f3f7bf46f8ad41fe82a07f4ac5d3c [file] [log] [blame]
[email protected]ba79eca2012-01-06 21:03:561// Copyright (c) 2012 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#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>
12#include <vector>
13
Lily Chen8ce89e42018-10-23 21:13:5814#include "base/bind.h"
15#include "base/callback.h"
Hans Wennborg0cb546b2020-06-23 18:00:0916#include "base/check_op.h"
Avi Drissman13fc8932015-12-20 04:40:4617#include "base/macros.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|.
135 explicit PriorityQueue(Priority num_priorities)
136 : lists_(num_priorities), size_(0) {
137#if !defined(NDEBUG)
138 next_id_ = 0;
139#endif
140 }
141
gab47aa7da2017-06-02 16:09:43142 ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
143
[email protected]ba79eca2012-01-06 21:03:56144 // Adds |value| with |priority| to the queue. Returns a pointer to the
145 // created element.
Lily Chen8ce89e42018-10-23 21:13:58146 Pointer Insert(T value, Priority priority) {
gab47aa7da2017-06-02 16:09:43147 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56148 DCHECK_LT(priority, lists_.size());
149 ++size_;
150 List& list = lists_[priority];
151#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:53152 unsigned id = next_id_;
[email protected]ba79eca2012-01-06 21:03:56153 valid_ids_.insert(id);
154 ++next_id_;
Lily Chen8ce89e42018-10-23 21:13:58155 list.emplace_back(std::make_pair(id, std::move(value)));
[email protected]ba79eca2012-01-06 21:03:56156#else
Lily Chen8ce89e42018-10-23 21:13:58157 list.emplace_back(std::move(value));
[email protected]ba79eca2012-01-06 21:03:56158#endif
Lily Chen8ce89e42018-10-23 21:13:58159 return Pointer(priority, std::prev(list.end()));
[email protected]ba79eca2012-01-06 21:03:56160 }
161
[email protected]daae1322013-09-05 18:26:50162 // Adds |value| with |priority| to the queue. Returns a pointer to the
163 // created element.
Lily Chen8ce89e42018-10-23 21:13:58164 Pointer InsertAtFront(T value, Priority priority) {
gab47aa7da2017-06-02 16:09:43165 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]daae1322013-09-05 18:26:50166 DCHECK_LT(priority, lists_.size());
167 ++size_;
168 List& list = lists_[priority];
169#if !defined(NDEBUG)
170 unsigned id = next_id_;
171 valid_ids_.insert(id);
172 ++next_id_;
Lily Chen8ce89e42018-10-23 21:13:58173 list.emplace_front(std::make_pair(id, std::move(value)));
[email protected]daae1322013-09-05 18:26:50174#else
Lily Chen8ce89e42018-10-23 21:13:58175 list.emplace_front(std::move(value));
[email protected]daae1322013-09-05 18:26:50176#endif
Lily Chen8ce89e42018-10-23 21:13:58177 return Pointer(priority, list.begin());
[email protected]daae1322013-09-05 18:26:50178 }
179
[email protected]ba79eca2012-01-06 21:03:56180 // Removes the value pointed by |pointer| from the queue. All pointers to this
Lily Chen8ce89e42018-10-23 21:13:58181 // value including |pointer| become invalid. Returns the erased value.
182 T Erase(const Pointer& pointer) {
gab47aa7da2017-06-02 16:09:43183 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56184 DCHECK_LT(pointer.priority_, lists_.size());
185 DCHECK_GT(size_, 0u);
186
187#if !defined(NDEBUG)
188 DCHECK_EQ(1u, valid_ids_.erase(pointer.id_));
189 DCHECK_EQ(pointer.iterator_->first, pointer.id_);
Lily Chen8ce89e42018-10-23 21:13:58190 T erased = std::move(pointer.iterator_->second);
191#else
192 T erased = std::move(*pointer.iterator_);
[email protected]ba79eca2012-01-06 21:03:56193#endif
194
195 --size_;
196 lists_[pointer.priority_].erase(pointer.iterator_);
Lily Chen8ce89e42018-10-23 21:13:58197 return erased;
[email protected]ba79eca2012-01-06 21:03:56198 }
199
200 // Returns a pointer to the first value of minimum priority or a null-pointer
201 // if empty.
[email protected]48118c32013-10-24 00:39:16202 Pointer FirstMin() const {
gab47aa7da2017-06-02 16:09:43203 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56204 for (size_t i = 0; i < lists_.size(); ++i) {
[email protected]48118c32013-10-24 00:39:16205 List* list = const_cast<List*>(&lists_[i]);
206 if (!list->empty())
207 return Pointer(i, list->begin());
[email protected]ba79eca2012-01-06 21:03:56208 }
209 return Pointer();
210 }
211
212 // Returns a pointer to the last value of minimum priority or a null-pointer
213 // if empty.
[email protected]48118c32013-10-24 00:39:16214 Pointer LastMin() const {
gab47aa7da2017-06-02 16:09:43215 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56216 for (size_t i = 0; i < lists_.size(); ++i) {
[email protected]48118c32013-10-24 00:39:16217 List* list = const_cast<List*>(&lists_[i]);
218 if (!list->empty())
219 return Pointer(i, --list->end());
[email protected]ba79eca2012-01-06 21:03:56220 }
221 return Pointer();
222 }
223
224 // Returns a pointer to the first value of maximum priority or a null-pointer
225 // if empty.
[email protected]48118c32013-10-24 00:39:16226 Pointer FirstMax() const {
gab47aa7da2017-06-02 16:09:43227 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56228 for (size_t i = lists_.size(); i > 0; --i) {
229 size_t index = i - 1;
[email protected]48118c32013-10-24 00:39:16230 List* list = const_cast<List*>(&lists_[index]);
231 if (!list->empty())
232 return Pointer(index, list->begin());
[email protected]ba79eca2012-01-06 21:03:56233 }
234 return Pointer();
235 }
236
237 // Returns a pointer to the last value of maximum priority or a null-pointer
238 // if empty.
[email protected]48118c32013-10-24 00:39:16239 Pointer LastMax() const {
gab47aa7da2017-06-02 16:09:43240 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56241 for (size_t i = lists_.size(); i > 0; --i) {
242 size_t index = i - 1;
[email protected]48118c32013-10-24 00:39:16243 List* list = const_cast<List*>(&lists_[index]);
244 if (!list->empty())
245 return Pointer(index, --list->end());
[email protected]ba79eca2012-01-06 21:03:56246 }
247 return Pointer();
248 }
249
Lily Chen8ce89e42018-10-23 21:13:58250 // Given an ordering of the values in this queue by decreasing priority and
251 // then FIFO, returns a pointer to the value following the value of the given
252 // pointer (which must be non-NULL). I.e., gets the next element in decreasing
253 // priority, then FIFO order. If the given pointer is already pointing at the
254 // last value, returns a null Pointer.
[email protected]48118c32013-10-24 00:39:16255 //
Lily Chen8ce89e42018-10-23 21:13:58256 // (One could also implement GetNextTowardsFirstMin() [decreasing priority,
257 // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing
258 // priority, then {,reverse} FIFO].)
[email protected]48118c32013-10-24 00:39:16259 Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
gab47aa7da2017-06-02 16:09:43260 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16261 DCHECK(!pointer.is_null());
262 DCHECK_LT(pointer.priority_, lists_.size());
263
264 typename Pointer::ListIterator it = pointer.iterator_;
265 Priority priority = pointer.priority_;
266 DCHECK(it != lists_[priority].end());
267 ++it;
268 while (it == lists_[priority].end()) {
Lily Chen8ce89e42018-10-23 21:13:58269 if (priority == 0u) {
270 DCHECK(pointer.Equals(LastMin()));
[email protected]48118c32013-10-24 00:39:16271 return Pointer();
Lily Chen8ce89e42018-10-23 21:13:58272 }
[email protected]48118c32013-10-24 00:39:16273 --priority;
274 it = const_cast<List*>(&lists_[priority])->begin();
275 }
276 return Pointer(priority, it);
277 }
278
Lily Chen8ce89e42018-10-23 21:13:58279 // Given an ordering of the values in this queue by decreasing priority and
280 // then FIFO, returns a pointer to the value preceding the value of the given
281 // pointer (which must be non-NULL). I.e., gets the next element in increasing
282 // priority, then reverse FIFO order. If the given pointer is already pointing
283 // at the first value, returns a null Pointer.
284 Pointer GetPreviousTowardsFirstMax(const Pointer& pointer) const {
285 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
286 DCHECK(!pointer.is_null());
287 DCHECK_LT(pointer.priority_, lists_.size());
288
289 typename Pointer::ListIterator it = pointer.iterator_;
290 Priority priority = pointer.priority_;
291 DCHECK(it != lists_[priority].end());
292 while (it == lists_[priority].begin()) {
293 if (priority == num_priorities() - 1) {
294 DCHECK(pointer.Equals(FirstMax()));
295 return Pointer();
296 }
297 ++priority;
298 it = const_cast<List*>(&lists_[priority])->end();
299 }
300 return Pointer(priority, std::prev(it));
301 }
302
303 // Checks whether |lhs| is closer in the queue to the first max element than
304 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
305 bool IsCloserToFirstMaxThan(const Pointer& lhs, const Pointer& rhs) {
306 if (lhs.Equals(rhs))
307 return false;
308 if (lhs.priority_ == rhs.priority_) {
309 // Traverse list starting from lhs and see if we find rhs.
310 for (auto it = lhs.iterator_; it != lists_[lhs.priority_].end(); ++it) {
311 if (it == rhs.iterator_)
312 return true;
313 }
314 return false;
315 }
316 return lhs.priority_ > rhs.priority_;
317 }
318
319 // Checks whether |lhs| is closer in the queue to the last min element than
320 // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
321 bool IsCloserToLastMinThan(const Pointer& lhs, const Pointer& rhs) {
322 return !lhs.Equals(rhs) && !IsCloserToFirstMaxThan(lhs, rhs);
323 }
324
325 // Finds the first element (with respect to decreasing priority, then FIFO
326 // order) which matches the given predicate.
327 Pointer FindIf(const base::RepeatingCallback<bool(T)>& pred) {
328 for (auto pointer = FirstMax(); !pointer.is_null();
329 pointer = GetNextTowardsLastMin(pointer)) {
330 if (pred.Run(pointer.value()))
331 return pointer;
332 }
333 return Pointer();
334 }
335
[email protected]ba79eca2012-01-06 21:03:56336 // Empties the queue. All pointers become invalid.
337 void Clear() {
gab47aa7da2017-06-02 16:09:43338 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56339 for (size_t i = 0; i < lists_.size(); ++i) {
340 lists_[i].clear();
341 }
342#if !defined(NDEBUG)
343 valid_ids_.clear();
344#endif
345 size_ = 0u;
346 }
347
[email protected]daae1322013-09-05 18:26:50348 // Returns the number of priorities the queue supports.
[email protected]48118c32013-10-24 00:39:16349 size_t num_priorities() const {
gab47aa7da2017-06-02 16:09:43350 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16351 return lists_.size();
352 }
353
354 bool empty() const {
gab47aa7da2017-06-02 16:09:43355 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]48118c32013-10-24 00:39:16356 return size_ == 0;
357 }
[email protected]daae1322013-09-05 18:26:50358
[email protected]ba79eca2012-01-06 21:03:56359 // Returns number of queued values.
360 size_t size() const {
gab47aa7da2017-06-02 16:09:43361 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]ba79eca2012-01-06 21:03:56362 return size_;
363 }
364
365 private:
366 typedef std::vector<List> ListVector;
367
368#if !defined(NDEBUG)
[email protected]043324f42012-06-02 00:18:53369 unsigned next_id_;
davidben1e912ea2016-04-20 19:17:07370 std::unordered_set<unsigned> valid_ids_;
[email protected]ba79eca2012-01-06 21:03:56371#endif
372
373 ListVector lists_;
374 size_t size_;
[email protected]043324f42012-06-02 00:18:53375
gab47aa7da2017-06-02 16:09:43376 THREAD_CHECKER(thread_checker_);
377
[email protected]043324f42012-06-02 00:18:53378 DISALLOW_COPY_AND_ASSIGN(PriorityQueue);
[email protected]ba79eca2012-01-06 21:03:56379};
380
381} // namespace net
382
383#endif // NET_BASE_PRIORITY_QUEUE_H_