| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_ | |
| 6 #define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_ | |
| 7 | |
| 8 #include <algorithm> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 | |
| 13 namespace blink { | |
| 14 namespace scheduler { | |
| 15 | |
| 16 template <typename T> | |
| 17 class IntrusiveHeap; | |
| 18 | |
| 19 // Intended as an opaque wrapper around |index_|. | |
| 20 class HeapHandle { | |
| 21 public: | |
| 22 HeapHandle() : index_(0u) {} | |
| 23 | |
| 24 bool IsValid() const { return index_ != 0u; } | |
| 25 | |
| 26 private: | |
| 27 template <typename T> | |
| 28 friend class IntrusiveHeap; | |
| 29 | |
| 30 HeapHandle(size_t index) : index_(index) {} | |
| 31 | |
| 32 size_t index_; | |
| 33 }; | |
| 34 | |
| 35 // A standard min-heap with the following assumptions: | |
| 36 // 1. T has operator <= | |
| 37 // 2. T has method void SetHeapHandle(HeapHandle handle) | |
| 38 // 3. T is moveable | |
| 39 // 4. T is default constructible | |
| 40 // 5. The heap size never gets terribly big so reclaiming memory on pop/erase | |
| 41 // isn't a priority. | |
| 42 // | |
| 43 // The reason IntrusiveHeap exists is to provide similar performance to | |
| 44 // std::priority_queue while allowing removal of arbitrary elements. | |
| 45 template <typename T> | |
| 46 class IntrusiveHeap { | |
| 47 public: | |
| 48 IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {} | |
| 49 | |
| 50 bool empty() const { return size_ == 0; } | |
| 51 | |
| 52 size_t size() const { return size_; } | |
| 53 | |
| 54 void clear() { | |
| 55 nodes_.resize(kMinimumHeapSize); | |
| 56 size_ = 0; | |
| 57 } | |
| 58 | |
| 59 const T& min() const { | |
| 60 DCHECK_GE(size_, 1u); | |
| 61 return nodes_[1]; | |
| 62 } | |
| 63 | |
| 64 void pop() { | |
| 65 DCHECK_GE(size_, 1u); | |
| 66 MakeHole(1u); | |
| 67 size_t top_index = size_--; | |
| 68 if (!empty()) | |
| 69 MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index])); | |
| 70 } | |
| 71 | |
| 72 void insert(T&& element) { | |
| 73 size_++; | |
| 74 if (size_ >= nodes_.size()) | |
| 75 nodes_.resize(nodes_.size() * 2); | |
| 76 // Notionally we have a hole in the tree at index |size_|, move this up | |
| 77 // to find the right insertion point. | |
| 78 MoveHoleUpAndFillWithElement(size_, std::move(element)); | |
| 79 } | |
| 80 | |
| 81 void erase(HeapHandle handle) { | |
| 82 DCHECK_GT(handle.index_, 0u); | |
| 83 DCHECK_LE(handle.index_, size_); | |
| 84 MakeHole(handle.index_); | |
| 85 size_t top_index = size_--; | |
| 86 if (empty() || top_index == handle.index_) | |
| 87 return; | |
| 88 if (nodes_[handle.index_] <= nodes_[top_index]) { | |
| 89 MoveHoleDownAndFillWithLeafElement(handle.index_, | |
| 90 std::move(nodes_[top_index])); | |
| 91 } else { | |
| 92 MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index])); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 void ReplaceMin(T&& element) { | |
| 97 // Note |element| might not be a leaf node so we can't use | |
| 98 // MoveHoleDownAndFillWithLeafElement. | |
| 99 MoveHoleDownAndFillWithElement(1u, std::move(element)); | |
| 100 } | |
| 101 | |
| 102 // Caution mutating the heap invalidates the iterators. | |
| 103 const T* begin() const { return &nodes_[1u]; } | |
| 104 const T* end() const { return begin() + size_; } | |
| 105 | |
| 106 private: | |
| 107 enum { | |
| 108 // The majority of sets in the scheduler have 0-3 items in them (a few will | |
| 109 // have perhaps up to 100), so this means we usually only have to allocate | |
| 110 // memory once. | |
| 111 kMinimumHeapSize = 4u | |
| 112 }; | |
| 113 | |
| 114 size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) { | |
| 115 DCHECK_GT(new_hole_pos, 0u); | |
| 116 DCHECK_LE(new_hole_pos, size_); | |
| 117 DCHECK_GT(new_hole_pos, 0u); | |
| 118 DCHECK_LE(new_hole_pos, size_); | |
| 119 DCHECK_NE(old_hole_pos, new_hole_pos); | |
| 120 nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]); | |
| 121 nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos)); | |
| 122 return new_hole_pos; | |
| 123 } | |
| 124 | |
| 125 // Notionally creates a hole in the tree at |index|. | |
| 126 void MakeHole(size_t index) { | |
| 127 DCHECK_GT(index, 0u); | |
| 128 DCHECK_LE(index, size_); | |
| 129 nodes_[index].SetHeapHandle(HeapHandle(0u)); | |
| 130 } | |
| 131 | |
| 132 void FillHole(size_t hole, T&& element) { | |
| 133 DCHECK_GT(hole, 0u); | |
| 134 DCHECK_LE(hole, size_); | |
| 135 nodes_[hole] = std::move(element); | |
| 136 nodes_[hole].SetHeapHandle(HeapHandle(hole)); | |
| 137 DCHECK(std::is_heap(begin(), end(), CompareNodes)); | |
| 138 } | |
| 139 | |
| 140 static bool CompareNodes(const T& a, const T& b) { return b <= a; } | |
| 141 | |
| 142 // Moves the |hole| up the tree and when the right position has been found | |
| 143 // |element| is moved in. | |
| 144 void MoveHoleUpAndFillWithElement(size_t hole, T&& element) { | |
| 145 DCHECK_GT(hole, 0u); | |
| 146 DCHECK_LE(hole, size_); | |
| 147 while (hole >= 2u) { | |
| 148 size_t parent_pos = hole / 2; | |
| 149 if (nodes_[parent_pos] <= element) | |
| 150 break; | |
| 151 | |
| 152 hole = MoveHole(parent_pos, hole); | |
| 153 } | |
| 154 FillHole(hole, std::move(element)); | |
| 155 } | |
| 156 | |
| 157 // Moves the |hole| down the tree and when the right position has been found | |
| 158 // |element| is moved in. | |
| 159 void MoveHoleDownAndFillWithElement(size_t hole, T&& element) { | |
| 160 DCHECK_GT(hole, 0u); | |
| 161 DCHECK_LE(hole, size_); | |
| 162 size_t child_pos = hole * 2; | |
| 163 while (child_pos < size_) { | |
| 164 if (nodes_[child_pos + 1] <= nodes_[child_pos]) | |
| 165 child_pos++; | |
| 166 | |
| 167 if (element <= nodes_[child_pos]) | |
| 168 break; | |
| 169 | |
| 170 hole = MoveHole(child_pos, hole); | |
| 171 child_pos *= 2; | |
| 172 } | |
| 173 if (child_pos == size_ && !(element <= nodes_[child_pos])) | |
| 174 hole = MoveHole(child_pos, hole); | |
| 175 FillHole(hole, std::move(element)); | |
| 176 } | |
| 177 | |
| 178 // Moves the |hole| down the tree and when the right position has been found | |
| 179 // |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement | |
| 180 // (it does one key comparison per level instead of two) but only valid for | |
| 181 // leaf elements (i.e. one of the max values). | |
| 182 void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) { | |
| 183 DCHECK_GT(hole, 0u); | |
| 184 DCHECK_LE(hole, size_); | |
| 185 size_t child_pos = hole * 2; | |
| 186 while (child_pos < size_) { | |
| 187 size_t second_child = child_pos + 1; | |
| 188 if (nodes_[second_child] <= nodes_[child_pos]) | |
| 189 child_pos = second_child; | |
| 190 | |
| 191 hole = MoveHole(child_pos, hole); | |
| 192 child_pos *= 2; | |
| 193 } | |
| 194 if (child_pos == size_) | |
| 195 hole = MoveHole(child_pos, hole); | |
| 196 MoveHoleUpAndFillWithElement(hole, std::move(leaf_element)); | |
| 197 } | |
| 198 | |
| 199 std::vector<T> nodes_; // NOTE we use 1-based indexing | |
| 200 size_t size_; | |
| 201 }; | |
| 202 | |
| 203 } // namespace scheduler | |
| 204 } // namespace blink | |
| 205 | |
| 206 #endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_ | |
| OLD | NEW |