Home | History | Annotate | Download | only in anomaly
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #pragma once
     18 
     19 #include <utils/RefBase.h>
     20 #include <unordered_map>
     21 #include <vector>
     22 
     23 using namespace android;
     24 
     25 namespace android {
     26 namespace os {
     27 namespace statsd {
     28 
     29 /** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
     30 template <class AA>
     31 struct SpHash {
     32     size_t operator()(const sp<const AA>& k) const {
     33         return std::hash<const AA*>()(k.get());
     34     }
     35 };
     36 
     37 /**
     38  * Min priority queue for generic type AA.
     39  * Unlike a regular priority queue, this class is also capable of removing interior elements.
     40  * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
     41  *    whether a should be closer to the top of the queue than b.
     42  */
     43 template <class AA, class Comparator>
     44 class indexed_priority_queue {
     45 public:
     46     indexed_priority_queue();
     47     /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */
     48     void push(sp<const AA> a);
     49     /*
     50      * Removes a from the priority queue. If not present or a==nullptr, does nothing.
     51      * Returns true if a had been present (and is now removed), else false.
     52      */
     53     bool remove(sp<const AA> a);
     54     /** Removes the top element, if there is one. */
     55     void pop();
     56     /** Removes all elements. */
     57     void clear();
     58     /** Returns whether priority queue contains a (not just a copy of a, but a itself). */
     59     bool contains(sp<const AA> a) const;
     60     /** Returns min element. Returns nullptr iff empty(). */
     61     sp<const AA> top() const;
     62     /** Returns number of elements in priority queue. */
     63     size_t size() const {
     64         return pq.size() - 1;
     65     }  // pq is 1-indexed
     66     /** Returns true iff priority queue is empty. */
     67     bool empty() const {
     68         return size() < 1;
     69     }
     70 
     71 private:
     72     /** Vector representing a min-heap (1-indexed, with nullptr at 0). */
     73     std::vector<sp<const AA>> pq;
     74     /** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */
     75     std::unordered_map<sp<const AA>, size_t, SpHash<AA>> indices;
     76 
     77     void init();
     78     void sift_up(size_t idx);
     79     void sift_down(size_t idx);
     80     /** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */
     81     bool higher(size_t idx1, size_t idx2) const;
     82     void swap_indices(size_t i, size_t j);
     83 };
     84 
     85 // Implementation must be done in this file due to use of template.
     86 
     87 template <class AA, class Comparator>
     88 indexed_priority_queue<AA, Comparator>::indexed_priority_queue() {
     89     init();
     90 }
     91 
     92 template <class AA, class Comparator>
     93 void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) {
     94     if (a == nullptr) return;
     95     if (contains(a)) return;
     96     pq.push_back(a);
     97     size_t idx = size();  // index of last element since 1-indexed
     98     indices.insert({a, idx});
     99     sift_up(idx);  // get the pq back in order
    100 }
    101 
    102 template <class AA, class Comparator>
    103 bool indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) {
    104     if (a == nullptr) return false;
    105     if (!contains(a)) return false;
    106     size_t idx = indices[a];
    107     if (idx >= pq.size()) {
    108         return false;
    109     }
    110     if (idx == size()) {  // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
    111         pq.pop_back();
    112         indices.erase(a);
    113         return true;
    114     }
    115     // move last element (guaranteed not to be at idx) to idx, then delete a
    116     sp<const AA> last_a = pq.back();
    117     pq[idx] = last_a;
    118     pq.pop_back();
    119     indices[last_a] = idx;
    120     indices.erase(a);
    121 
    122     // get the heap back in order (since the element at idx is not in order)
    123     sift_up(idx);
    124     sift_down(idx);
    125 
    126     return true;
    127 }
    128 
    129 // The same as, but slightly more efficient than, remove(top()).
    130 template <class AA, class Comparator>
    131 void indexed_priority_queue<AA, Comparator>::pop() {
    132     sp<const AA> a = top();
    133     if (a == nullptr) return;
    134     const size_t idx = 1;
    135     if (idx == size()) {  // if a is the last element
    136         pq.pop_back();
    137         indices.erase(a);
    138         return;
    139     }
    140     // move last element (guaranteed not to be at idx) to idx, then delete a
    141     sp<const AA> last_a = pq.back();
    142     pq[idx] = last_a;
    143     pq.pop_back();
    144     indices[last_a] = idx;
    145     indices.erase(a);
    146 
    147     // get the heap back in order (since the element at idx is not in order)
    148     sift_down(idx);
    149 }
    150 
    151 template <class AA, class Comparator>
    152 void indexed_priority_queue<AA, Comparator>::clear() {
    153     pq.clear();
    154     indices.clear();
    155     init();
    156 }
    157 
    158 template <class AA, class Comparator>
    159 sp<const AA> indexed_priority_queue<AA, Comparator>::top() const {
    160     if (empty()) return nullptr;
    161     return pq[1];
    162 }
    163 
    164 template <class AA, class Comparator>
    165 void indexed_priority_queue<AA, Comparator>::init() {
    166     pq.push_back(nullptr);         // so that pq is 1-indexed.
    167     indices.insert({nullptr, 0});  // just to be consistent with pq.
    168 }
    169 
    170 template <class AA, class Comparator>
    171 void indexed_priority_queue<AA, Comparator>::sift_up(size_t idx) {
    172     while (idx > 1) {
    173         size_t parent = idx / 2;
    174         if (higher(idx, parent))
    175             swap_indices(idx, parent);
    176         else
    177             break;
    178         idx = parent;
    179     }
    180 }
    181 
    182 template <class AA, class Comparator>
    183 void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) {
    184     while (2 * idx <= size()) {
    185         size_t child = 2 * idx;
    186         if (child < size() && higher(child + 1, child)) child++;
    187         if (higher(child, idx))
    188             swap_indices(child, idx);
    189         else
    190             break;
    191         idx = child;
    192     }
    193 }
    194 
    195 template <class AA, class Comparator>
    196 bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
    197     if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
    198         return false;  // got to do something.
    199     }
    200     return Comparator()(pq[idx1], pq[idx2]);
    201 }
    202 
    203 template <class AA, class Comparator>
    204 bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const {
    205     if (a == nullptr) return false;  // publicly, we pretend that nullptr is not actually in pq.
    206     return indices.count(a) > 0;
    207 }
    208 
    209 template <class AA, class Comparator>
    210 void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
    211     if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
    212         return;
    213     }
    214     sp<const AA> val_i = pq[i];
    215     sp<const AA> val_j = pq[j];
    216     pq[i] = val_j;
    217     pq[j] = val_i;
    218     indices[val_i] = j;
    219     indices[val_j] = i;
    220 }
    221 
    222 }  // namespace statsd
    223 }  // namespace os
    224 }  // namespace android
    225