xref: /aosp_15_r20/external/cronet/net/base/priority_queue.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <list>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/check_op.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/threading/thread_checker.h"
19 
20 #if !defined(NDEBUG)
21 #include <unordered_set>
22 #endif
23 
24 namespace 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 //
35 template <typename T>
36 class PriorityQueue {
37  private:
38   // This section is up-front for Pointer only.
39 #if !defined(NDEBUG)
40   typedef std::list<std::pair<unsigned, T> > List;
41 #else
42   typedef std::list<T> List;
43 #endif
44 
45  public:
46   typedef uint32_t Priority;
47 
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.
Pointer()53     Pointer() : priority_(kNullPriority) {
54 #if !defined(NDEBUG)
55       id_ = static_cast<unsigned>(-1);
56 #endif
57       // 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();
62     }
63 
Pointer(const Pointer & p)64     Pointer(const Pointer& p)
65         : priority_(p.priority_),
66           iterator_(p.iterator_) {
67 #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 
is_null()82     bool is_null() const { return priority_ == kNullPriority; }
83 
priority()84     Priority priority() const { return priority_; }
85 
86 #if !defined(NDEBUG)
value()87     const T& value() const { return iterator_->second; }
88 #else
value()89     const T& value() const { return *iterator_; }
90 #endif
91 
92     // Comparing to Pointer from a different PriorityQueue is undefined.
Equals(const Pointer & other)93     bool Equals(const Pointer& other) const {
94       return (priority_ == other.priority_) && (iterator_ == other.iterator_);
95     }
96 
Reset()97     void Reset() {
98       *this = Pointer();
99     }
100 
101    private:
102     friend class PriorityQueue;
103 
104     // 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.
108     typedef typename PriorityQueue::List::iterator ListIterator;
109 
110     static const Priority kNullPriority = static_cast<Priority>(-1);
111 
112     // It is guaranteed that Pointer will treat |iterator| as a
113     // const_iterator.
Pointer(Priority priority,const ListIterator & iterator)114     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_;
123     // 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_;
127 
128 #if !defined(NDEBUG)
129     // Used by the queue to check if a Pointer is valid.
130     unsigned id_;
131 #endif
132   };
133 
134   // Creates a new queue for |num_priorities|.
PriorityQueue(Priority num_priorities)135   explicit PriorityQueue(Priority num_priorities) : lists_(num_priorities) {
136 #if !defined(NDEBUG)
137     next_id_ = 0;
138 #endif
139   }
140 
141   PriorityQueue(const PriorityQueue&) = delete;
142   PriorityQueue& operator=(const PriorityQueue&) = delete;
~PriorityQueue()143   ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
144 
145   // Adds |value| with |priority| to the queue. Returns a pointer to the
146   // created element.
Insert(T value,Priority priority)147   Pointer Insert(T value, Priority priority) {
148     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
149     DCHECK_LT(priority, lists_.size());
150     ++size_;
151     List& list = lists_[priority];
152 #if !defined(NDEBUG)
153     unsigned id = next_id_;
154     valid_ids_.insert(id);
155     ++next_id_;
156     list.emplace_back(id, std::move(value));
157 #else
158     list.emplace_back(std::move(value));
159 #endif
160     return Pointer(priority, std::prev(list.end()));
161   }
162 
163   // Adds |value| with |priority| to the queue. Returns a pointer to the
164   // created element.
InsertAtFront(T value,Priority priority)165   Pointer InsertAtFront(T value, Priority priority) {
166     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
167     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_;
174     list.emplace_front(std::pair(id, std::move(value)));
175 #else
176     list.emplace_front(std::move(value));
177 #endif
178     return Pointer(priority, list.begin());
179   }
180 
181   // Removes the value pointed by |pointer| from the queue. All pointers to this
182   // value including |pointer| become invalid. Returns the erased value.
Erase(const Pointer & pointer)183   T Erase(const Pointer& pointer) {
184     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
185     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_);
191     T erased = std::move(pointer.iterator_->second);
192 #else
193     T erased = std::move(*pointer.iterator_);
194 #endif
195 
196     --size_;
197     lists_[pointer.priority_].erase(pointer.iterator_);
198     return erased;
199   }
200 
201   // Returns a pointer to the first value of minimum priority or a null-pointer
202   // if empty.
FirstMin()203   Pointer FirstMin() const {
204     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
205     for (size_t i = 0; i < lists_.size(); ++i) {
206       List* list = const_cast<List*>(&lists_[i]);
207       if (!list->empty())
208         return Pointer(i, list->begin());
209     }
210     return Pointer();
211   }
212 
213   // Returns a pointer to the last value of minimum priority or a null-pointer
214   // if empty.
LastMin()215   Pointer LastMin() const {
216     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
217     for (size_t i = 0; i < lists_.size(); ++i) {
218       List* list = const_cast<List*>(&lists_[i]);
219       if (!list->empty())
220         return Pointer(i, --list->end());
221     }
222     return Pointer();
223   }
224 
225   // Returns a pointer to the first value of maximum priority or a null-pointer
226   // if empty.
FirstMax()227   Pointer FirstMax() const {
228     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
229     for (size_t i = lists_.size(); i > 0; --i) {
230       size_t index = i - 1;
231       List* list = const_cast<List*>(&lists_[index]);
232       if (!list->empty())
233         return Pointer(index, list->begin());
234     }
235     return Pointer();
236   }
237 
238   // Returns a pointer to the last value of maximum priority or a null-pointer
239   // if empty.
LastMax()240   Pointer LastMax() const {
241     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
242     for (size_t i = lists_.size(); i > 0; --i) {
243       size_t index = i - 1;
244       List* list = const_cast<List*>(&lists_[index]);
245       if (!list->empty())
246         return Pointer(index, --list->end());
247     }
248     return Pointer();
249   }
250 
251   // 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.
256   //
257   // (One could also implement GetNextTowardsFirstMin() [decreasing priority,
258   // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing
259   // priority, then {,reverse} FIFO].)
GetNextTowardsLastMin(const Pointer & pointer)260   Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
261     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
262     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()) {
270       if (priority == 0u) {
271         DCHECK(pointer.Equals(LastMin()));
272         return Pointer();
273       }
274       --priority;
275       it = const_cast<List*>(&lists_[priority])->begin();
276     }
277     return Pointer(priority, it);
278   }
279 
280   // 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.
GetPreviousTowardsFirstMax(const Pointer & 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.
IsCloserToFirstMaxThan(const Pointer & lhs,const Pointer & rhs)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.
IsCloserToLastMinThan(const Pointer & lhs,const Pointer & rhs)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.
FindIf(const base::RepeatingCallback<bool (T)> & pred)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 
337   // Empties the queue. All pointers become invalid.
Clear()338   void Clear() {
339     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
340     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 
349   // Returns the number of priorities the queue supports.
num_priorities()350   size_t num_priorities() const {
351     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
352     return lists_.size();
353   }
354 
empty()355   bool empty() const {
356     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
357     return size_ == 0;
358   }
359 
360   // Returns number of queued values.
size()361   size_t size() const {
362     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
363     return size_;
364   }
365 
366  private:
367   typedef std::vector<List> ListVector;
368 
369 #if !defined(NDEBUG)
370   unsigned next_id_;
371   std::unordered_set<unsigned> valid_ids_;
372 #endif
373 
374   ListVector lists_;
375   size_t size_ = 0;
376 
377   THREAD_CHECKER(thread_checker_);
378 };
379 
380 }  // namespace net
381 
382 #endif  // NET_BASE_PRIORITY_QUEUE_H_
383