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 has method void ClearHeapHandle() |
| 39 // 4. T is moveable |
| 40 // 5. T is default constructible |
| 41 // 6. The heap size never gets terribly big so reclaiming memory on pop/erase |
| 42 // isn't a priority. |
| 43 // |
| 44 // The reason IntrusiveHeap exists is to provide similar performance to |
| 45 // std::priority_queue while allowing removal of arbitrary elements. |
| 46 template <typename T> |
| 47 class IntrusiveHeap { |
| 48 public: |
| 49 IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {} |
| 50 |
| 51 ~IntrusiveHeap() { |
| 52 for (size_t i = 1; i <= size_; i++) { |
| 53 MakeHole(i); |
| 54 } |
| 55 } |
| 56 |
| 57 bool empty() const { return size_ == 0; } |
| 58 |
| 59 size_t size() const { return size_; } |
| 60 |
| 61 void clear() { |
| 62 for (size_t i = 1; i <= size_; i++) { |
| 63 MakeHole(i); |
| 64 } |
| 65 nodes_.resize(kMinimumHeapSize); |
| 66 size_ = 0; |
| 67 } |
| 68 |
| 69 const T& min() const { |
| 70 DCHECK_GE(size_, 1u); |
| 71 return nodes_[1]; |
| 72 } |
| 73 |
| 74 void pop() { |
| 75 DCHECK_GE(size_, 1u); |
| 76 MakeHole(1u); |
| 77 size_t top_index = size_--; |
| 78 if (!empty()) |
| 79 MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index])); |
| 80 } |
| 81 |
| 82 void insert(T&& element) { |
| 83 size_++; |
| 84 if (size_ >= nodes_.size()) |
| 85 nodes_.resize(nodes_.size() * 2); |
| 86 // Notionally we have a hole in the tree at index |size_|, move this up |
| 87 // to find the right insertion point. |
| 88 MoveHoleUpAndFillWithElement(size_, std::move(element)); |
| 89 } |
| 90 |
| 91 void erase(HeapHandle handle) { |
| 92 DCHECK_GT(handle.index_, 0u); |
| 93 DCHECK_LE(handle.index_, size_); |
| 94 MakeHole(handle.index_); |
| 95 size_t top_index = size_--; |
| 96 if (empty() || top_index == handle.index_) |
| 97 return; |
| 98 if (nodes_[handle.index_] <= nodes_[top_index]) { |
| 99 MoveHoleDownAndFillWithLeafElement(handle.index_, |
| 100 std::move(nodes_[top_index])); |
| 101 } else { |
| 102 MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index])); |
| 103 } |
| 104 } |
| 105 |
| 106 void ReplaceMin(T&& element) { |
| 107 // Note |element| might not be a leaf node so we can't use |
| 108 // MoveHoleDownAndFillWithLeafElement. |
| 109 MoveHoleDownAndFillWithElement(1u, std::move(element)); |
| 110 } |
| 111 |
| 112 void ChangeKey(HeapHandle handle, T&& element) { |
| 113 if (nodes_[handle.index_] <= element) { |
| 114 MoveHoleDownAndFillWithLeafElement(handle.index_, std::move(element)); |
| 115 } else { |
| 116 MoveHoleUpAndFillWithElement(handle.index_, std::move(element)); |
| 117 } |
| 118 } |
| 119 |
| 120 // Caution mutating the heap invalidates the iterators. |
| 121 const T* begin() const { return &nodes_[1u]; } |
| 122 const T* end() const { return begin() + size_; } |
| 123 |
| 124 private: |
| 125 enum { |
| 126 // The majority of sets in the scheduler have 0-3 items in them (a few will |
| 127 // have perhaps up to 100), so this means we usually only have to allocate |
| 128 // memory once. |
| 129 kMinimumHeapSize = 4u |
| 130 }; |
| 131 |
| 132 size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) { |
| 133 DCHECK_GT(new_hole_pos, 0u); |
| 134 DCHECK_LE(new_hole_pos, size_); |
| 135 DCHECK_GT(new_hole_pos, 0u); |
| 136 DCHECK_LE(new_hole_pos, size_); |
| 137 DCHECK_NE(old_hole_pos, new_hole_pos); |
| 138 nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]); |
| 139 nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos)); |
| 140 return new_hole_pos; |
| 141 } |
| 142 |
| 143 // Notionally creates a hole in the tree at |index|. |
| 144 void MakeHole(size_t index) { |
| 145 DCHECK_GT(index, 0u); |
| 146 DCHECK_LE(index, size_); |
| 147 nodes_[index].ClearHeapHandle(); |
| 148 } |
| 149 |
| 150 void FillHole(size_t hole, T&& element) { |
| 151 DCHECK_GT(hole, 0u); |
| 152 DCHECK_LE(hole, size_); |
| 153 nodes_[hole] = std::move(element); |
| 154 nodes_[hole].SetHeapHandle(HeapHandle(hole)); |
| 155 DCHECK(std::is_heap(begin(), end(), CompareNodes)); |
| 156 } |
| 157 |
| 158 static bool CompareNodes(const T& a, const T& b) { return b <= a; } |
| 159 |
| 160 // Moves the |hole| up the tree and when the right position has been found |
| 161 // |element| is moved in. |
| 162 void MoveHoleUpAndFillWithElement(size_t hole, T&& element) { |
| 163 DCHECK_GT(hole, 0u); |
| 164 DCHECK_LE(hole, size_); |
| 165 while (hole >= 2u) { |
| 166 size_t parent_pos = hole / 2; |
| 167 if (nodes_[parent_pos] <= element) |
| 168 break; |
| 169 |
| 170 hole = MoveHole(parent_pos, hole); |
| 171 } |
| 172 FillHole(hole, std::move(element)); |
| 173 } |
| 174 |
| 175 // Moves the |hole| down the tree and when the right position has been found |
| 176 // |element| is moved in. |
| 177 void MoveHoleDownAndFillWithElement(size_t hole, T&& element) { |
| 178 DCHECK_GT(hole, 0u); |
| 179 DCHECK_LE(hole, size_); |
| 180 size_t child_pos = hole * 2; |
| 181 while (child_pos < size_) { |
| 182 if (nodes_[child_pos + 1] <= nodes_[child_pos]) |
| 183 child_pos++; |
| 184 |
| 185 if (element <= nodes_[child_pos]) |
| 186 break; |
| 187 |
| 188 hole = MoveHole(child_pos, hole); |
| 189 child_pos *= 2; |
| 190 } |
| 191 if (child_pos == size_ && !(element <= nodes_[child_pos])) |
| 192 hole = MoveHole(child_pos, hole); |
| 193 FillHole(hole, std::move(element)); |
| 194 } |
| 195 |
| 196 // Moves the |hole| down the tree and when the right position has been found |
| 197 // |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement |
| 198 // (it does one key comparison per level instead of two) but only valid for |
| 199 // leaf elements (i.e. one of the max values). |
| 200 void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) { |
| 201 DCHECK_GT(hole, 0u); |
| 202 DCHECK_LE(hole, size_); |
| 203 size_t child_pos = hole * 2; |
| 204 while (child_pos < size_) { |
| 205 size_t second_child = child_pos + 1; |
| 206 if (nodes_[second_child] <= nodes_[child_pos]) |
| 207 child_pos = second_child; |
| 208 |
| 209 hole = MoveHole(child_pos, hole); |
| 210 child_pos *= 2; |
| 211 } |
| 212 if (child_pos == size_) |
| 213 hole = MoveHole(child_pos, hole); |
| 214 MoveHoleUpAndFillWithElement(hole, std::move(leaf_element)); |
| 215 } |
| 216 |
| 217 std::vector<T> nodes_; // NOTE we use 1-based indexing |
| 218 size_t size_; |
| 219 }; |
| 220 |
| 221 } // namespace scheduler |
| 222 } // namespace blink |
| 223 |
| 224 #endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_ |
OLD | NEW |