blob: 5f6fd3bc73ebc1efb6ba212dc1012b87bd868d0c [file] [log] [blame]
bsalomon3555bd82015-02-13 11:08:21 -08001/*
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 "SkTDArray.h"
Derek Sollenberger5480a182017-05-25 16:43:59 -040012#include "SkTSort.h"
bsalomon3555bd82015-02-13 11:08:21 -080013
14/**
15 * This class implements a priority queue. T is the type of the elements in the queue. LESS is a
16 * function that compares two Ts and returns true if the first is higher priority than the second.
17 *
18 * Optionally objects may know their index into the priority queue. The queue will update the index
halcanary96fcdcc2015-08-27 07:41:13 -070019 * as the objects move through the queue. This is enabled by using a non-nullptr function for INDEX.
bsalomon3555bd82015-02-13 11:08:21 -080020 * When an INDEX function is provided random deletes from the queue are allowed using remove().
21 * Additionally, the * priority is allowed to change as long as priorityDidChange() is called
22 * afterwards. In debug builds the index will be set to -1 before an element is removed from the
23 * queue.
24 */
25template <typename T,
26 bool (*LESS)(const T&, const T&),
halcanary96fcdcc2015-08-27 07:41:13 -070027 int* (*INDEX)(const T&) = (int* (*)(const T&))nullptr>
Mike Kleinbc4c96b2017-05-03 15:55:10 -040028class SkTDPQueue {
bsalomon3555bd82015-02-13 11:08:21 -080029public:
30 SkTDPQueue() {}
31
Mike Kleinbc4c96b2017-05-03 15:55:10 -040032 SkTDPQueue(SkTDPQueue&&) = default;
33 SkTDPQueue& operator =(SkTDPQueue&&) = default;
34
35 SkTDPQueue(const SkTDPQueue&) = delete;
36 SkTDPQueue& operator=(const SkTDPQueue&) = delete;
37
bsalomon3555bd82015-02-13 11:08:21 -080038 /** Number of items in the queue. */
39 int count() const { return fArray.count(); }
40
41 /** Gets the next item in the queue without popping it. */
42 const T& peek() const { return fArray[0]; }
43 T& peek() { return fArray[0]; }
halcanary9d524f22016-03-29 09:03:52 -070044
bsalomon3555bd82015-02-13 11:08:21 -080045 /** Removes the next item. */
46 void pop() {
47 this->validate();
48 SkDEBUGCODE(if (SkToBool(INDEX)) { *INDEX(fArray[0]) = -1; })
49 if (1 == fArray.count()) {
50 fArray.pop();
51 return;
52 }
53
54 fArray[0] = fArray[fArray.count() - 1];
55 this->setIndex(0);
56 fArray.pop();
57 this->percolateDownIfNecessary(0);
58
59 this->validate();
60 }
61
62 /** Inserts a new item in the queue based on its priority. */
63 void insert(T entry) {
64 this->validate();
65 int index = fArray.count();
66 *fArray.append() = entry;
67 this->setIndex(fArray.count() - 1);
68 this->percolateUpIfNecessary(index);
69 this->validate();
70 }
71
halcanary96fcdcc2015-08-27 07:41:13 -070072 /** Random access removal. This requires that the INDEX function is non-nullptr. */
bsalomon3555bd82015-02-13 11:08:21 -080073 void remove(T entry) {
halcanary96fcdcc2015-08-27 07:41:13 -070074 SkASSERT(nullptr != INDEX);
bsalomon3555bd82015-02-13 11:08:21 -080075 int index = *INDEX(entry);
76 SkASSERT(index >= 0 && index < fArray.count());
77 this->validate();
78 SkDEBUGCODE(*INDEX(fArray[index]) = -1;)
79 if (index == fArray.count() - 1) {
80 fArray.pop();
81 return;
82 }
83 fArray[index] = fArray[fArray.count() - 1];
84 fArray.pop();
85 this->setIndex(index);
86 this->percolateUpOrDown(index);
87 this->validate();
88 }
89
90 /** Notification that the priority of an entry has changed. This must be called after an
91 item's priority is changed to maintain correct ordering. Changing the priority is only
92 allowed if an INDEX function is provided. */
93 void priorityDidChange(T entry) {
halcanary96fcdcc2015-08-27 07:41:13 -070094 SkASSERT(nullptr != INDEX);
bsalomon3555bd82015-02-13 11:08:21 -080095 int index = *INDEX(entry);
96 SkASSERT(index >= 0 && index < fArray.count());
97 this->validate(index);
98 this->percolateUpOrDown(index);
99 this->validate();
100 }
101
bsalomon1a9600f2015-02-23 10:59:50 -0800102 /** Gets the item at index i in the priority queue (for i < this->count()). at(0) is equivalent
103 to peek(). Otherwise, there is no guarantee about ordering of elements in the queue. */
bsalomon9f2d1572015-02-17 11:47:40 -0800104 T at(int i) const { return fArray[i]; }
bsalomon9f2d1572015-02-17 11:47:40 -0800105
Derek Sollenberger5480a182017-05-25 16:43:59 -0400106 /** Sorts the queue into priority order. The queue is only guarenteed to remain in sorted order
107 * until any other operation, other than at(), is performed.
108 */
109 void sort() {
110 if (fArray.count() > 1) {
111 SkTQSort<T>(fArray.begin(), fArray.end() - 1, LESS);
112 for (int i = 0; i < fArray.count(); i++) {
113 this->setIndex(i);
114 }
115 this->validate();
116 }
117 }
118
bsalomon3555bd82015-02-13 11:08:21 -0800119private:
120 static int LeftOf(int x) { SkASSERT(x >= 0); return 2 * x + 1; }
121 static int ParentOf(int x) { SkASSERT(x > 0); return (x - 1) >> 1; }
122
123 void percolateUpOrDown(int index) {
124 SkASSERT(index >= 0);
125 if (!percolateUpIfNecessary(index)) {
126 this->validate(index);
127 this->percolateDownIfNecessary(index);
128 }
129 }
130
131 bool percolateUpIfNecessary(int index) {
132 SkASSERT(index >= 0);
133 bool percolated = false;
134 do {
135 if (0 == index) {
136 this->setIndex(index);
137 return percolated;
138 }
139 int p = ParentOf(index);
140 if (LESS(fArray[index], fArray[p])) {
141 SkTSwap(fArray[index], fArray[p]);
142 this->setIndex(index);
143 index = p;
144 percolated = true;
145 } else {
146 this->setIndex(index);
147 return percolated;
148 }
149 this->validate(index);
150 } while (true);
151 }
152
153 void percolateDownIfNecessary(int index) {
154 SkASSERT(index >= 0);
155 do {
156 int child = LeftOf(index);
halcanary9d524f22016-03-29 09:03:52 -0700157
bsalomon3555bd82015-02-13 11:08:21 -0800158 if (child >= fArray.count()) {
159 // We're a leaf.
160 this->setIndex(index);
161 return;
162 }
163
164 if (child + 1 >= fArray.count()) {
165 // We only have a left child.
166 if (LESS(fArray[child], fArray[index])) {
167 SkTSwap(fArray[child], fArray[index]);
168 this->setIndex(child);
169 this->setIndex(index);
170 return;
171 }
172 } else if (LESS(fArray[child + 1], fArray[child])) {
173 // The right child is the one we should swap with, if we swap.
174 child++;
175 }
176
177 // Check if we need to swap.
178 if (LESS(fArray[child], fArray[index])) {
179 SkTSwap(fArray[child], fArray[index]);
180 this->setIndex(index);
181 index = child;
182 } else {
183 // We're less than both our children.
184 this->setIndex(index);
185 return;
186 }
187 this->validate(index);
188 } while (true);
189 }
190
191 void setIndex(int index) {
192 SkASSERT(index < fArray.count());
193 if (SkToBool(INDEX)) {
194 *INDEX(fArray[index]) = index;
195 }
196 }
197
198 void validate(int excludedIndex = -1) const {
199#ifdef SK_DEBUG
200 for (int i = 1; i < fArray.count(); ++i) {
201 int p = ParentOf(i);
202 if (excludedIndex != p && excludedIndex != i) {
203 SkASSERT(!(LESS(fArray[i], fArray[p])));
204 SkASSERT(!SkToBool(INDEX) || *INDEX(fArray[i]) == i);
205 }
206 }
207#endif
208 }
209
210 SkTDArray<T> fArray;
bsalomon3555bd82015-02-13 11:08:21 -0800211};
212
213#endif