xref: /aosp_15_r20/external/skia/src/base/SkTDPQueue.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkTDPQueue_DEFINED
9 #define SkTDPQueue_DEFINED
10 
11 #include "include/private/base/SkAssert.h"
12 #include "include/private/base/SkDebug.h"
13 #include "include/private/base/SkTDArray.h"
14 #include "include/private/base/SkTo.h"
15 #include "src/base/SkTSort.h"
16 
17 #include <utility>
18 
19 /**
20  * This class implements a priority queue. T is the type of the elements in the queue. LESS is a
21  * function that compares two Ts and returns true if the first is higher priority than the second.
22  *
23  * Optionally objects may know their index into the priority queue. The queue will update the index
24  * as the objects move through the queue. This is enabled by using a non-nullptr function for INDEX.
25  * When an INDEX function is provided random deletes from the queue are allowed using remove().
26  * Additionally, the * priority is allowed to change as long as priorityDidChange() is called
27  * afterwards. In debug builds the index will be set to -1 before an element is removed from the
28  * queue.
29  */
30 template <typename T,
31           bool (*LESS)(const T&, const T&),
32           int* (*INDEX)(const T&) = (int* (*)(const T&))nullptr>
33 class SkTDPQueue {
34 public:
SkTDPQueue()35     SkTDPQueue() {}
SkTDPQueue(int reserve)36     SkTDPQueue(int reserve) { fArray.reserve(reserve); }
37 
38     SkTDPQueue(SkTDPQueue&&) = default;
39     SkTDPQueue& operator =(SkTDPQueue&&) = default;
40 
41     SkTDPQueue(const SkTDPQueue&) = delete;
42     SkTDPQueue& operator=(const SkTDPQueue&) = delete;
43 
44     /** Number of items in the queue. */
count()45     int count() const { return fArray.size(); }
46 
47     /** Gets the next item in the queue without popping it. */
peek()48     const T& peek() const { return fArray[0]; }
peek()49     T& peek() { return fArray[0]; }
50 
51     /** Removes the next item. */
pop()52     void pop() {
53         this->validate();
54         SkDEBUGCODE(if (SkToBool(INDEX)) { *INDEX(fArray[0]) = -1; })
55         if (1 == fArray.size()) {
56             fArray.pop_back();
57             return;
58         }
59 
60         fArray[0] = fArray[fArray.size() - 1];
61         this->setIndex(0);
62         fArray.pop_back();
63         this->percolateDownIfNecessary(0);
64 
65         this->validate();
66     }
67 
68     /** Inserts a new item in the queue based on its priority. */
insert(T entry)69     void insert(T entry) {
70         this->validate();
71         int index = fArray.size();
72         *fArray.append() = entry;
73         this->setIndex(fArray.size() - 1);
74         this->percolateUpIfNecessary(index);
75         this->validate();
76     }
77 
78     /** Random access removal. This requires that the INDEX function is non-nullptr. */
remove(T entry)79     void remove(T entry) {
80         SkASSERT(nullptr != INDEX);
81         int index = *INDEX(entry);
82         SkASSERT(index >= 0 && index < fArray.size());
83         this->validate();
84         SkDEBUGCODE(*INDEX(fArray[index]) = -1;)
85         if (index == fArray.size() - 1) {
86             fArray.pop_back();
87             return;
88         }
89         fArray[index] = fArray[fArray.size() - 1];
90         fArray.pop_back();
91         this->setIndex(index);
92         this->percolateUpOrDown(index);
93         this->validate();
94     }
95 
96     /** Notification that the priority of an entry has changed. This must be called after an
97         item's priority is changed to maintain correct ordering. Changing the priority is only
98         allowed if an INDEX function is provided. */
priorityDidChange(T entry)99     void priorityDidChange(T entry) {
100         SkASSERT(nullptr != INDEX);
101         int index = *INDEX(entry);
102         SkASSERT(index >= 0 && index < fArray.size());
103         this->validate(index);
104         this->percolateUpOrDown(index);
105         this->validate();
106     }
107 
108     /** Gets the item at index i in the priority queue (for i < this->count()). at(0) is equivalent
109         to peek(). Otherwise, there is no guarantee about ordering of elements in the queue. */
at(int i)110     T at(int i) const { return fArray[i]; }
111 
112     /** Sorts the queue into priority order.  The queue is only guarenteed to remain in sorted order
113      *  until any other operation, other than at(), is performed.
114      */
sort()115     void sort() {
116         if (fArray.size() > 1) {
117             SkTQSort<T>(fArray.begin(), fArray.end(), LESS);
118             for (int i = 0; i < fArray.size(); i++) {
119                 this->setIndex(i);
120             }
121             this->validate();
122         }
123     }
124 
125 private:
LeftOf(int x)126     static int LeftOf(int x) { SkASSERT(x >= 0); return 2 * x + 1; }
ParentOf(int x)127     static int ParentOf(int x) { SkASSERT(x > 0); return (x - 1) >> 1; }
128 
percolateUpOrDown(int index)129     void percolateUpOrDown(int index) {
130         SkASSERT(index >= 0);
131         if (!percolateUpIfNecessary(index)) {
132             this->validate(index);
133             this->percolateDownIfNecessary(index);
134         }
135     }
136 
percolateUpIfNecessary(int index)137     bool percolateUpIfNecessary(int index) {
138         SkASSERT(index >= 0);
139         bool percolated = false;
140         do {
141             if (0 == index) {
142                 this->setIndex(index);
143                 return percolated;
144             }
145             int p = ParentOf(index);
146             if (LESS(fArray[index], fArray[p])) {
147                 using std::swap;
148                 swap(fArray[index], fArray[p]);
149                 this->setIndex(index);
150                 index = p;
151                 percolated = true;
152             } else {
153                 this->setIndex(index);
154                 return percolated;
155             }
156             this->validate(index);
157         } while (true);
158     }
159 
percolateDownIfNecessary(int index)160     void percolateDownIfNecessary(int index) {
161         SkASSERT(index >= 0);
162         do {
163             int child = LeftOf(index);
164 
165             if (child >= fArray.size()) {
166                 // We're a leaf.
167                 this->setIndex(index);
168                 return;
169             }
170 
171             if (child + 1 >= fArray.size()) {
172                 // We only have a left child.
173                 if (LESS(fArray[child], fArray[index])) {
174                     using std::swap;
175                     swap(fArray[child], fArray[index]);
176                     this->setIndex(child);
177                     this->setIndex(index);
178                     return;
179                 }
180             } else if (LESS(fArray[child + 1], fArray[child])) {
181                 // The right child is the one we should swap with, if we swap.
182                 child++;
183             }
184 
185             // Check if we need to swap.
186             if (LESS(fArray[child], fArray[index])) {
187                 using std::swap;
188                 swap(fArray[child], fArray[index]);
189                 this->setIndex(index);
190                 index = child;
191             } else {
192                 // We're less than both our children.
193                 this->setIndex(index);
194                 return;
195             }
196             this->validate(index);
197         } while (true);
198     }
199 
setIndex(int index)200     void setIndex(int index) {
201         SkASSERT(index < fArray.size());
202         if (SkToBool(INDEX)) {
203             *INDEX(fArray[index]) = index;
204         }
205     }
206 
207     void validate(int excludedIndex = -1) const {
208 #ifdef SK_DEBUG
209         for (int i = 1; i < fArray.size(); ++i) {
210             int p = ParentOf(i);
211             if (excludedIndex != p && excludedIndex != i) {
212                 SkASSERT(!(LESS(fArray[i], fArray[p])));
213                 SkASSERT(!SkToBool(INDEX) || *INDEX(fArray[i]) == i);
214             }
215         }
216 #endif
217     }
218 
219     SkTDArray<T> fArray;
220 };
221 
222 #endif
223