Flutter Engine
The Flutter Engine
priority_queue.h
Go to the documentation of this file.
1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
6#define RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
7
8#include "platform/assert.h"
9#include "platform/globals.h"
10#include "platform/hashmap.h"
11#include "platform/utils.h"
12
13namespace dart {
14
15// A min-priority queue with deletion support.
16//
17// The [PriorityQueue] allows insertion of entries with a priority [P] and a
18// value [V]. The minimum element can be queried in O(1) time.
19// Insertion/Deletion operations have O(N) time.
20//
21// In addition to the normal insert/minimum/remove-minimum operations this
22// priority queue allows deletion-by-value. We have therefore an invariant
23// is that the value must be unique amongst all entries.
24template <typename P, typename V>
26 public:
27 static constexpr intptr_t kMinimumSize = 16;
28
29 struct Entry {
32 };
33
34 PriorityQueue() : hashmap_(&MatchFun, kMinimumSize) {
35 min_heap_size_ = kMinimumSize;
36 min_heap_ =
37 reinterpret_cast<Entry*>(malloc(sizeof(Entry) * min_heap_size_));
38 if (min_heap_ == nullptr) FATAL("Cannot allocate memory.");
39 size_ = 0;
40 }
41
42 ~PriorityQueue() { free(min_heap_); }
43
44 // Whether the queue is empty.
45 bool IsEmpty() const { return size_ == 0; }
46
47 // Inserts a new entry with [priority] and [value], requires there to be no
48 // existing entry with given [value].
49 void Insert(const P& priority, const V& value) {
51
52 if (size_ == min_heap_size_) {
53 Resize(min_heap_size_ << 1);
54 }
55
56 Set(size_, {priority, value});
57 BubbleUp(size_);
58
59 size_++;
60 }
61
62 // Returns a reference to the minimum entry.
63 //
64 // The caller can access it's priority and value in read-only mode only.
65 const Entry& Minimum() const {
66 ASSERT(!IsEmpty());
67 return min_heap_[0];
68 }
69
70 // Removes the minimum entry.
72 ASSERT(!IsEmpty());
73 RemoveAt(0);
74 }
75
76 // Removes an existing entry with the given [value].
77 //
78 // Returns true if such an entry was removed.
79 bool RemoveByValue(const V& value) {
80 auto entry = FindMapEntry(value);
81 if (entry != nullptr) {
82 const intptr_t offset = ValueOfMapEntry(entry);
83 RemoveAt(offset);
84
85 ASSERT(hashmap_.size() == size_);
86 return true;
87 }
88 return false;
89 }
90
91 // Whether the priority queue contains an entry with the given [value].
92 bool ContainsValue(const V& value) { return FindMapEntry(value) != nullptr; }
93
94 // Changes the priority of an existing entry with given [value] or adds a
95 // new entry.
96 bool InsertOrChangePriority(const P& priority, const V& value) {
97 auto map_entry = FindMapEntry(value);
98 if (map_entry == nullptr) {
99 Insert(priority, value);
100 return true;
101 }
102
103 const intptr_t offset = ValueOfMapEntry(map_entry);
104 ASSERT(offset < size_);
105
106 Entry& entry = min_heap_[offset];
107 entry.priority = priority;
108 if (offset == 0) {
109 BubbleDown(offset);
110 } else {
111 intptr_t parent = (offset - 1) / 2;
112 intptr_t diff = entry.priority - min_heap_[parent].priority;
113 if (diff < 0) {
114 BubbleUp(offset);
115 } else if (diff > 0) {
116 BubbleDown(offset);
117 }
118 }
119 return false;
120 }
121
122#ifdef TESTING
123 intptr_t min_heap_size() { return min_heap_size_; }
124#endif // TESTING
125
126 private:
127 // Utility functions dealing with the SimpleHashMap interface.
128 static bool MatchFun(void* key1, void* key2) { return key1 == key2; }
129
130 SimpleHashMap::Entry* FindMapEntry(const V& key, bool insert = false) {
131 return hashmap_.Lookup(CastKey(key), HashKey(key), insert);
132 }
133 void RemoveMapEntry(const V& key) {
134 ASSERT(FindMapEntry(key) != nullptr);
135 hashmap_.Remove(CastKey(key), HashKey(key));
136 }
137 void SetMapEntry(const V& key, intptr_t value) {
138 FindMapEntry(key, /*insert=*/true)->value = reinterpret_cast<void*>(value);
139 }
140 static uint32_t HashKey(const V& key) {
141 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(CastKey(key)));
142 }
143 static intptr_t ValueOfMapEntry(SimpleHashMap::Entry* entry) {
144 return reinterpret_cast<intptr_t>(entry->value);
145 }
146 static void* CastKey(const V& key) {
147 return reinterpret_cast<void*>((const_cast<V&>(key)));
148 }
149
150 void RemoveAt(intptr_t offset) {
151 ASSERT(offset < size_);
152
153 size_--;
154
155 if (offset == size_) {
156 RemoveMapEntry(min_heap_[offset].value);
157 } else {
158 Replace(offset, size_);
159 BubbleDown(offset);
160 }
161
162 if (size_ <= (min_heap_size_ >> 2) &&
163 kMinimumSize <= (min_heap_size_ >> 1)) {
164 Resize(min_heap_size_ >> 1);
165 }
166 }
167
168 void BubbleUp(intptr_t offset) {
169 while (true) {
170 if (offset == 0) return;
171
172 intptr_t parent = (offset - 1) / 2;
173 if (min_heap_[parent].priority > min_heap_[offset].priority) {
174 Swap(parent, offset);
175 }
176 offset = parent;
177 }
178 }
179
180 void BubbleDown(intptr_t offset) {
181 while (true) {
182 intptr_t left_child_index = 2 * offset + 1;
183 bool has_left_child = left_child_index < size_;
184
185 if (!has_left_child) return;
186
187 intptr_t smallest_index = offset;
188
189 if (min_heap_[left_child_index].priority < min_heap_[offset].priority) {
190 smallest_index = left_child_index;
191 }
192
193 intptr_t right_child_index = left_child_index + 1;
194 bool has_right_child = right_child_index < size_;
195 if (has_right_child) {
196 if (min_heap_[right_child_index].priority <
197 min_heap_[smallest_index].priority) {
198 smallest_index = right_child_index;
199 }
200 }
201
202 if (offset == smallest_index) {
203 return;
204 }
205
206 Swap(offset, smallest_index);
207 offset = smallest_index;
208 }
209 }
210
211 void Set(intptr_t offset1, const Entry& entry) {
212 min_heap_[offset1] = entry;
213 SetMapEntry(entry.value, offset1);
214 }
215
216 void Swap(intptr_t offset1, intptr_t offset2) {
217 Entry temp = min_heap_[offset1];
218 min_heap_[offset1] = min_heap_[offset2];
219 min_heap_[offset2] = temp;
220
221 SetMapEntry(min_heap_[offset1].value, offset1);
222 SetMapEntry(min_heap_[offset2].value, offset2);
223 }
224
225 void Replace(intptr_t index, intptr_t with_other) {
226 RemoveMapEntry(min_heap_[index].value);
227
228 const Entry& entry = min_heap_[with_other];
229 SetMapEntry(entry.value, index);
230 min_heap_[index] = entry;
231 }
232
233 void Resize(intptr_t new_min_heap_size) {
234 ASSERT(size_ < new_min_heap_size);
235 ASSERT(new_min_heap_size != min_heap_size_);
236
237 Entry* new_backing = reinterpret_cast<Entry*>(
238 realloc(min_heap_, sizeof(Entry) * new_min_heap_size));
239
240 if (new_backing == nullptr) FATAL("Cannot allocate memory.");
241
242 min_heap_ = new_backing;
243 min_heap_size_ = new_min_heap_size;
244 }
245
246 // The array is representing a tree structure with guaranteed log(n) height.
247 // It has the property that the value of node N is always equal or smaller
248 // than the value of N's children. Furthermore it is a "dense" tree in the
249 // sense that all rows/layers of the tree are fully occupied except the last
250 // one. The way to represent such "dense" trees is via an array that allows
251 // finding left/right children by <2*index+1><2*index+2> and the parent by
252 // <(index-1)/2>.
253 //
254 // Insertion operations can be performed by adding one more entry at the end
255 // (bottom right) and bubbling it up until the tree invariant is satisfied
256 // again.
257 //
258 // Deletion operations can be performed by replacing the minimum element
259 // (first entry) by the last entry (bottom right) and bubbling it down until
260 // the tree invariant is satisfied again.
261 Entry* min_heap_;
262 intptr_t min_heap_size_;
263 intptr_t size_;
264 SimpleHashMap hashmap_;
265};
266
267} // namespace dart
268
269#endif // RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
bool RemoveByValue(const V &value)
const Entry & Minimum() const
bool ContainsValue(const V &value)
static constexpr intptr_t kMinimumSize
void Insert(const P &priority, const V &value)
bool InsertOrChangePriority(const P &priority, const V &value)
bool IsEmpty() const
Entry * Lookup(void *key, uint32_t hash, bool insert)
Definition: hashmap.cc:20
intptr_t size() const
Definition: hashmap.h:74
void Remove(void *key, uint32_t hash)
Definition: hashmap.cc:49
#define ASSERT(E)
#define FATAL(error)
uint8_t value
T __attribute__((ext_vector_type(N))) V
Definition: dart_vm.cc:33
void * malloc(size_t size)
Definition: allocation.cc:19
void * realloc(void *ptr, size_t size)
Definition: allocation.cc:27
SeparatedVector2 offset