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