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