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