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 |