1 // 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_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <list>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/bind.h"
16 #include "base/callback.h"
17 #include "base/check_op.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)
136       : lists_(num_priorities), size_(0) {
137 #if !defined(NDEBUG)
138     next_id_ = 0;
139 #endif
140   }
141 
142   PriorityQueue(const PriorityQueue&) = delete;
143   PriorityQueue& operator=(const PriorityQueue&) = delete;
~PriorityQueue()144   ~PriorityQueue() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
145 
146   // Adds |value| with |priority| to the queue. Returns a pointer to the
147   // created element.
Insert(T value,Priority priority)148   Pointer Insert(T value, Priority priority) {
149     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
150     DCHECK_LT(priority, lists_.size());
151     ++size_;
152     List& list = lists_[priority];
153 #if !defined(NDEBUG)
154     unsigned id = next_id_;
155     valid_ids_.insert(id);
156     ++next_id_;
157     list.emplace_back(std::make_pair(id, std::move(value)));
158 #else
159     list.emplace_back(std::move(value));
160 #endif
161     return Pointer(priority, std::prev(list.end()));
162   }
163 
164   // Adds |value| with |priority| to the queue. Returns a pointer to the
165   // created element.
InsertAtFront(T value,Priority priority)166   Pointer InsertAtFront(T value, Priority priority) {
167     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
168     DCHECK_LT(priority, lists_.size());
169     ++size_;
170     List& list = lists_[priority];
171 #if !defined(NDEBUG)
172     unsigned id = next_id_;
173     valid_ids_.insert(id);
174     ++next_id_;
175     list.emplace_front(std::make_pair(id, std::move(value)));
176 #else
177     list.emplace_front(std::move(value));
178 #endif
179     return Pointer(priority, list.begin());
180   }
181 
182   // Removes the value pointed by |pointer| from the queue. All pointers to this
183   // value including |pointer| become invalid. Returns the erased value.
Erase(const Pointer & pointer)184   T Erase(const Pointer& pointer) {
185     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
186     DCHECK_LT(pointer.priority_, lists_.size());
187     DCHECK_GT(size_, 0u);
188 
189 #if !defined(NDEBUG)
190     DCHECK_EQ(1u, valid_ids_.erase(pointer.id_));
191     DCHECK_EQ(pointer.iterator_->first, pointer.id_);
192     T erased = std::move(pointer.iterator_->second);
193 #else
194     T erased = std::move(*pointer.iterator_);
195 #endif
196 
197     --size_;
198     lists_[pointer.priority_].erase(pointer.iterator_);
199     return erased;
200   }
201 
202   // Returns a pointer to the first value of minimum priority or a null-pointer
203   // if empty.
FirstMin()204   Pointer FirstMin() const {
205     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
206     for (size_t i = 0; i < lists_.size(); ++i) {
207       List* list = const_cast<List*>(&lists_[i]);
208       if (!list->empty())
209         return Pointer(i, list->begin());
210     }
211     return Pointer();
212   }
213 
214   // Returns a pointer to the last value of minimum priority or a null-pointer
215   // if empty.
LastMin()216   Pointer LastMin() const {
217     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
218     for (size_t i = 0; i < lists_.size(); ++i) {
219       List* list = const_cast<List*>(&lists_[i]);
220       if (!list->empty())
221         return Pointer(i, --list->end());
222     }
223     return Pointer();
224   }
225 
226   // Returns a pointer to the first value of maximum priority or a null-pointer
227   // if empty.
FirstMax()228   Pointer FirstMax() const {
229     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
230     for (size_t i = lists_.size(); i > 0; --i) {
231       size_t index = i - 1;
232       List* list = const_cast<List*>(&lists_[index]);
233       if (!list->empty())
234         return Pointer(index, list->begin());
235     }
236     return Pointer();
237   }
238 
239   // Returns a pointer to the last value of maximum priority or a null-pointer
240   // if empty.
LastMax()241   Pointer LastMax() const {
242     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
243     for (size_t i = lists_.size(); i > 0; --i) {
244       size_t index = i - 1;
245       List* list = const_cast<List*>(&lists_[index]);
246       if (!list->empty())
247         return Pointer(index, --list->end());
248     }
249     return Pointer();
250   }
251 
252   // Given an ordering of the values in this queue by decreasing priority and
253   // then FIFO, returns a pointer to the value following the value of the given
254   // pointer (which must be non-NULL). I.e., gets the next element in decreasing
255   // priority, then FIFO order. If the given pointer is already pointing at the
256   // last value, returns a null Pointer.
257   //
258   // (One could also implement GetNextTowardsFirstMin() [decreasing priority,
259   // then reverse FIFO] as well as GetNextTowards{First,Last}Max() [increasing
260   // priority, then {,reverse} FIFO].)
GetNextTowardsLastMin(const Pointer & pointer)261   Pointer GetNextTowardsLastMin(const Pointer& pointer) const {
262     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
263     DCHECK(!pointer.is_null());
264     DCHECK_LT(pointer.priority_, lists_.size());
265 
266     typename Pointer::ListIterator it = pointer.iterator_;
267     Priority priority = pointer.priority_;
268     DCHECK(it != lists_[priority].end());
269     ++it;
270     while (it == lists_[priority].end()) {
271       if (priority == 0u) {
272         DCHECK(pointer.Equals(LastMin()));
273         return Pointer();
274       }
275       --priority;
276       it = const_cast<List*>(&lists_[priority])->begin();
277     }
278     return Pointer(priority, it);
279   }
280 
281   // Given an ordering of the values in this queue by decreasing priority and
282   // then FIFO, returns a pointer to the value preceding the value of the given
283   // pointer (which must be non-NULL). I.e., gets the next element in increasing
284   // priority, then reverse FIFO order. If the given pointer is already pointing
285   // at the first value, returns a null Pointer.
GetPreviousTowardsFirstMax(const Pointer & pointer)286   Pointer GetPreviousTowardsFirstMax(const Pointer& pointer) const {
287     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
288     DCHECK(!pointer.is_null());
289     DCHECK_LT(pointer.priority_, lists_.size());
290 
291     typename Pointer::ListIterator it = pointer.iterator_;
292     Priority priority = pointer.priority_;
293     DCHECK(it != lists_[priority].end());
294     while (it == lists_[priority].begin()) {
295       if (priority == num_priorities() - 1) {
296         DCHECK(pointer.Equals(FirstMax()));
297         return Pointer();
298       }
299       ++priority;
300       it = const_cast<List*>(&lists_[priority])->end();
301     }
302     return Pointer(priority, std::prev(it));
303   }
304 
305   // Checks whether |lhs| is closer in the queue to the first max element than
306   // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
IsCloserToFirstMaxThan(const Pointer & lhs,const Pointer & rhs)307   bool IsCloserToFirstMaxThan(const Pointer& lhs, const Pointer& rhs) {
308     if (lhs.Equals(rhs))
309       return false;
310     if (lhs.priority_ == rhs.priority_) {
311       // Traverse list starting from lhs and see if we find rhs.
312       for (auto it = lhs.iterator_; it != lists_[lhs.priority_].end(); ++it) {
313         if (it == rhs.iterator_)
314           return true;
315       }
316       return false;
317     }
318     return lhs.priority_ > rhs.priority_;
319   }
320 
321   // Checks whether |lhs| is closer in the queue to the last min element than
322   // |rhs|. Assumes that both Pointers refer to elements in this PriorityQueue.
IsCloserToLastMinThan(const Pointer & lhs,const Pointer & rhs)323   bool IsCloserToLastMinThan(const Pointer& lhs, const Pointer& rhs) {
324     return !lhs.Equals(rhs) && !IsCloserToFirstMaxThan(lhs, rhs);
325   }
326 
327   // Finds the first element (with respect to decreasing priority, then FIFO
328   // order) which matches the given predicate.
FindIf(const base::RepeatingCallback<bool (T)> & pred)329   Pointer FindIf(const base::RepeatingCallback<bool(T)>& pred) {
330     for (auto pointer = FirstMax(); !pointer.is_null();
331          pointer = GetNextTowardsLastMin(pointer)) {
332       if (pred.Run(pointer.value()))
333         return pointer;
334     }
335     return Pointer();
336   }
337 
338   // Empties the queue. All pointers become invalid.
Clear()339   void Clear() {
340     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
341     for (size_t i = 0; i < lists_.size(); ++i) {
342       lists_[i].clear();
343     }
344 #if !defined(NDEBUG)
345     valid_ids_.clear();
346 #endif
347     size_ = 0u;
348   }
349 
350   // Returns the number of priorities the queue supports.
num_priorities()351   size_t num_priorities() const {
352     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
353     return lists_.size();
354   }
355 
empty()356   bool empty() const {
357     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
358     return size_ == 0;
359   }
360 
361   // Returns number of queued values.
size()362   size_t size() const {
363     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
364     return size_;
365   }
366 
367  private:
368   typedef std::vector<List> ListVector;
369 
370 #if !defined(NDEBUG)
371   unsigned next_id_;
372   std::unordered_set<unsigned> valid_ids_;
373 #endif
374 
375   ListVector lists_;
376   size_t size_;
377 
378   THREAD_CHECKER(thread_checker_);
379 };
380 
381 }  // namespace net
382 
383 #endif  // NET_BASE_PRIORITY_QUEUE_H_
384