1 // Copyright 2019 The Chromium Authors
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 BASE_CONTAINERS_INTRUSIVE_HEAP_H_
6 #define BASE_CONTAINERS_INTRUSIVE_HEAP_H_
7
8 // Implements a standard max-heap, but with arbitrary element removal. To
9 // facilitate this, each element has associated with it a HeapHandle (an opaque
10 // wrapper around the index at which the element is stored), which is maintained
11 // by the heap as elements move within it.
12 //
13 // An IntrusiveHeap is implemented as a standard max-heap over a std::vector<T>,
14 // like std::make_heap. Insertion, removal and updating are amortized O(lg size)
15 // (occasional O(size) cost if a new vector allocation is required). Retrieving
16 // an element by handle is O(1). Looking up the top element is O(1). Insertions,
17 // removals and updates invalidate all iterators, but handles remain valid.
18 // Similar to a std::set, all iterators are read-only so as to disallow changing
19 // elements and violating the heap property. That being said, if the type you
20 // are storing is able to have its sort key be changed externally you can
21 // repair the heap by resorting the modified element via a call to "Update".
22 //
23 // Example usage:
24 //
25 // // Create a heap, wrapping integer elements with WithHeapHandle in order to
26 // // endow them with heap handles.
27 // IntrusiveHeap<WithHeapHandle<int>> heap;
28 //
29 // // WithHeapHandle<T> is for simple or opaque types. In cases where you
30 // // control the type declaration you can also provide HeapHandle storage by
31 // // deriving from InternalHeapHandleStorage.
32 // class Foo : public InternalHeapHandleStorage {
33 // public:
34 // explicit Foo(int);
35 // ...
36 // };
37 // IntrusiveHeap<Foo> heap2;
38 //
39 // // Insert some elements. Like most containers, "insert" returns an iterator
40 // // to the element in the container.
41 // heap.insert(3);
42 // heap.insert(1);
43 // auto it = heap.insert(4);
44 //
45 // // By default this is a max heap, so the top element should be 4 at this
46 // // point.
47 // EXPECT_EQ(4, heap.top().value());
48 //
49 // // Iterators are invalidated by further heap operations, but handles are
50 // // not. Grab a handle to the current top element so we can track it across
51 // // changes.
52 // HeapHandle* handle = it->handle();
53 //
54 // // Insert a new max element. 4 should no longer be the top.
55 // heap.insert(5);
56 // EXPECT_EQ(5, heap.top().value());
57 //
58 // // We can lookup and erase element 4 by its handle, even though it has
59 // // moved. Note that erasing the element invalidates the handle to it.
60 // EXPECT_EQ(4, heap.at(*handle).value());
61 // heap.erase(*handle);
62 // handle = nullptr;
63 //
64 // // Popping the current max (5), makes 3 the new max, as we already erased
65 // // element 4.
66 // heap.pop();
67 // EXPECT_EQ(3, heap.top().value());
68 //
69 // Under the hood the HeapHandle is managed by an object implementing the
70 // HeapHandleAccess interface, which is passed as a parameter to the
71 // IntrusiveHeap template:
72 //
73 // // Gets the heap handle associated with the element. This should return the
74 // // most recently set handle value, or HeapHandle::Invalid(). This is only
75 // // called in DCHECK builds.
76 // HeapHandle GetHeapHandle(const T*);
77 //
78 // // Changes the result of GetHeapHandle. GetHeapHandle() must return the
79 // // most recent value provided to SetHeapHandle() or HeapHandle::Invalid().
80 // // In some implementations, where GetHeapHandle() can independently
81 // // reproduce the correct value, it is possible that SetHeapHandle() does
82 // // nothing.
83 // void SetHeapHandle(T*, HeapHandle);
84 //
85 // // Clears the heap handle associated with the given element. After calling
86 // // this GetHeapHandle() must return HeapHandle::Invalid().
87 // void ClearHeapHandle(T*);
88 //
89 // The default implementation of HeapHandleAccess assumes that your type
90 // provides HeapHandle storage and will simply forward these calls to equivalent
91 // member functions on the type T:
92 //
93 // void T::SetHeapHandle(HeapHandle)
94 // void T::ClearHeapHandle()
95 // HeapHandle T::GetHeapHandle() const
96 //
97 // The WithHeapHandle and InternalHeapHandleStorage classes in turn provide
98 // implementations of that contract.
99 //
100 // In summary, to provide heap handle support for your type, you can do one of
101 // the following (from most manual / least magical, to least manual / most
102 // magical):
103 //
104 // 0. use a custom HeapHandleAccessor, and implement storage however you want;
105 // 1. use the default HeapHandleAccessor, and manually provide storage on your
106 // your element type and implement the IntrusiveHeap contract;
107 // 2. use the default HeapHandleAccessor, and endow your type with handle
108 // storage by deriving from a helper class (see InternalHeapHandleStorage);
109 // or,
110 // 3. use the default HeapHandleAccessor, and wrap your type in a container that
111 // provides handle storage (see WithHeapHandle<T>).
112 //
113 // Approach 0 is suitable for custom types that already implement something akin
114 // to heap handles, via back pointers or any other mechanism, but where the
115 // storage is external to the objects in the heap. If you already have the
116 // ability to determine where in a container an object lives despite it
117 // being moved, then you don't need the overhead of storing an actual HeapHandle
118 // whose value can be inferred.
119 //
120 // Approach 1 is is suitable in cases like the above, but where the data
121 // allowing you to determine the index of an element in a container is stored
122 // directly in the object itself.
123 //
124 // Approach 2 is suitable for types whose declarations you control, where you
125 // are able to use inheritance.
126 //
127 // Finally, approach 3 is suitable when you are storing PODs, or a type whose
128 // declaration you can not change.
129 //
130 // Most users should be using approach 2 or 3.
131
132 #include <algorithm>
133 #include <functional>
134 #include <limits>
135 #include <memory>
136 #include <type_traits>
137 #include <utility>
138 #include <vector>
139
140 #include "base/base_export.h"
141 #include "base/check.h"
142 #include "base/check_op.h"
143 #include "base/containers/stack_container.h"
144 #include "base/memory/ptr_util.h"
145 #include "base/ranges/algorithm.h"
146
147 namespace base {
148
149 // Intended as a wrapper around an |index_| in the vector storage backing an
150 // IntrusiveHeap. A HeapHandle is associated with each element in an
151 // IntrusiveHeap, and is maintained by the heap as the object moves around
152 // within it. It can be used to subsequently remove the element, or update it
153 // in place.
154 class BASE_EXPORT HeapHandle {
155 public:
156 enum : size_t { kInvalidIndex = std::numeric_limits<size_t>::max() };
157
158 constexpr HeapHandle() = default;
159 constexpr HeapHandle(const HeapHandle& other) = default;
HeapHandle(HeapHandle && other)160 HeapHandle(HeapHandle&& other) noexcept
161 : index_(std::exchange(other.index_, kInvalidIndex)) {}
162 ~HeapHandle() = default;
163
164 HeapHandle& operator=(const HeapHandle& other) = default;
165 HeapHandle& operator=(HeapHandle&& other) noexcept {
166 index_ = std::exchange(other.index_, kInvalidIndex);
167 return *this;
168 }
169
170 static HeapHandle Invalid();
171
172 // Resets this handle back to an invalid state.
reset()173 void reset() { index_ = kInvalidIndex; }
174
175 // Accessors.
index()176 size_t index() const { return index_; }
IsValid()177 bool IsValid() const { return index_ != kInvalidIndex; }
178
179 // Comparison operators.
180 friend bool operator==(const HeapHandle& lhs, const HeapHandle& rhs) {
181 return lhs.index_ == rhs.index_;
182 }
183 friend bool operator!=(const HeapHandle& lhs, const HeapHandle& rhs) {
184 return lhs.index_ != rhs.index_;
185 }
186 friend bool operator<(const HeapHandle& lhs, const HeapHandle& rhs) {
187 return lhs.index_ < rhs.index_;
188 }
189 friend bool operator>(const HeapHandle& lhs, const HeapHandle& rhs) {
190 return lhs.index_ > rhs.index_;
191 }
192 friend bool operator<=(const HeapHandle& lhs, const HeapHandle& rhs) {
193 return lhs.index_ <= rhs.index_;
194 }
195 friend bool operator>=(const HeapHandle& lhs, const HeapHandle& rhs) {
196 return lhs.index_ >= rhs.index_;
197 }
198
199 private:
200 template <typename T, typename Compare, typename HeapHandleAccessor>
201 friend class IntrusiveHeap;
202
203 // Only IntrusiveHeaps can create valid HeapHandles.
HeapHandle(size_t index)204 explicit HeapHandle(size_t index) : index_(index) {}
205
206 size_t index_ = kInvalidIndex;
207 };
208
209 // The default HeapHandleAccessor, which simply forwards calls to the underlying
210 // type.
211 template <typename T>
212 struct DefaultHeapHandleAccessor {
SetHeapHandleDefaultHeapHandleAccessor213 void SetHeapHandle(T* element, HeapHandle handle) const {
214 element->SetHeapHandle(handle);
215 }
216
ClearHeapHandleDefaultHeapHandleAccessor217 void ClearHeapHandle(T* element) const { element->ClearHeapHandle(); }
218
GetHeapHandleDefaultHeapHandleAccessor219 HeapHandle GetHeapHandle(const T* element) const {
220 return element->GetHeapHandle();
221 }
222 };
223
224 // Intrusive heap class. This is something like a std::vector (insertion and
225 // removal are similar, objects don't have a fixed address in memory) crossed
226 // with a std::set (elements are considered immutable once they're in the
227 // container).
228 template <typename T,
229 typename Compare = std::less<T>,
230 typename HeapHandleAccessor = DefaultHeapHandleAccessor<T>>
231 class IntrusiveHeap {
232 private:
233 using UnderlyingType = std::vector<T>;
234
235 public:
236 //////////////////////////////////////////////////////////////////////////////
237 // Types.
238
239 using value_type = typename UnderlyingType::value_type;
240 using size_type = typename UnderlyingType::size_type;
241 using difference_type = typename UnderlyingType::difference_type;
242 using value_compare = Compare;
243 using heap_handle_accessor = HeapHandleAccessor;
244
245 using reference = typename UnderlyingType::reference;
246 using const_reference = typename UnderlyingType::const_reference;
247 using pointer = typename UnderlyingType::pointer;
248 using const_pointer = typename UnderlyingType::const_pointer;
249
250 // Iterators are read-only.
251 using iterator = typename UnderlyingType::const_iterator;
252 using const_iterator = typename UnderlyingType::const_iterator;
253 using reverse_iterator = typename UnderlyingType::const_reverse_iterator;
254 using const_reverse_iterator =
255 typename UnderlyingType::const_reverse_iterator;
256
257 //////////////////////////////////////////////////////////////////////////////
258 // Lifetime.
259
260 IntrusiveHeap() = default;
IntrusiveHeap(const value_compare & comp,const heap_handle_accessor & access)261 IntrusiveHeap(const value_compare& comp, const heap_handle_accessor& access)
262 : impl_(comp, access) {}
263
264 template <class InputIterator>
265 IntrusiveHeap(InputIterator first,
266 InputIterator last,
267 const value_compare& comp = value_compare(),
268 const heap_handle_accessor& access = heap_handle_accessor())
impl_(comp,access)269 : impl_(comp, access) {
270 insert(first, last);
271 }
272
273 // Moves an intrusive heap. The outstanding handles remain valid and end up
274 // pointing to the new heap.
275 IntrusiveHeap(IntrusiveHeap&& other) = default;
276
277 // Copy constructor for an intrusive heap.
278 IntrusiveHeap(const IntrusiveHeap&);
279
280 // Initializer list constructor.
281 template <typename U>
282 IntrusiveHeap(std::initializer_list<U> ilist,
283 const value_compare& comp = value_compare(),
284 const heap_handle_accessor& access = heap_handle_accessor())
impl_(comp,access)285 : impl_(comp, access) {
286 insert(std::begin(ilist), std::end(ilist));
287 }
288
289 ~IntrusiveHeap();
290
291 //////////////////////////////////////////////////////////////////////////////
292 // Assignment.
293
294 IntrusiveHeap& operator=(IntrusiveHeap&&) noexcept;
295 IntrusiveHeap& operator=(const IntrusiveHeap&);
296 IntrusiveHeap& operator=(std::initializer_list<value_type> ilist);
297
298 //////////////////////////////////////////////////////////////////////////////
299 // Element access.
300 //
301 // These provide O(1) const access to the elements in the heap. If you wish to
302 // modify an element in the heap you should first remove it from the heap, and
303 // then reinsert it into the heap, or use the "Replace*" helper functions. In
304 // the rare case where you directly modify an element in the heap you can
305 // subsequently repair the heap with "Update".
306
at(size_type pos)307 const_reference at(size_type pos) const { return impl_.heap_.at(pos); }
at(HeapHandle pos)308 const_reference at(HeapHandle pos) const {
309 return impl_.heap_.at(pos.index());
310 }
311 const_reference operator[](size_type pos) const { return impl_.heap_[pos]; }
312 const_reference operator[](HeapHandle pos) const {
313 return impl_.heap_[pos.index()];
314 }
front()315 const_reference front() const { return impl_.heap_.front(); }
back()316 const_reference back() const { return impl_.heap_.back(); }
top()317 const_reference top() const { return impl_.heap_.front(); }
318
319 // May or may not return a null pointer if size() is zero.
data()320 const_pointer data() const { return impl_.heap_.data(); }
321
322 //////////////////////////////////////////////////////////////////////////////
323 // Memory management.
324
reserve(size_type new_capacity)325 void reserve(size_type new_capacity) { impl_.heap_.reserve(new_capacity); }
capacity()326 size_type capacity() const { return impl_.heap_.capacity(); }
shrink_to_fit()327 void shrink_to_fit() { impl_.heap_.shrink_to_fit(); }
328
329 //////////////////////////////////////////////////////////////////////////////
330 // Size management.
331
332 void clear();
size()333 size_type size() const { return impl_.heap_.size(); }
max_size()334 size_type max_size() const { return impl_.heap_.max_size(); }
empty()335 bool empty() const { return impl_.heap_.empty(); }
336
337 //////////////////////////////////////////////////////////////////////////////
338 // Iterators.
339 //
340 // Only constant iterators are allowed.
341
begin()342 const_iterator begin() const { return impl_.heap_.cbegin(); }
cbegin()343 const_iterator cbegin() const { return impl_.heap_.cbegin(); }
344
end()345 const_iterator end() const { return impl_.heap_.cend(); }
cend()346 const_iterator cend() const { return impl_.heap_.cend(); }
347
rbegin()348 const_reverse_iterator rbegin() const { return impl_.heap_.crbegin(); }
crbegin()349 const_reverse_iterator crbegin() const { return impl_.heap_.crbegin(); }
350
rend()351 const_reverse_iterator rend() const { return impl_.heap_.crend(); }
crend()352 const_reverse_iterator crend() const { return impl_.heap_.crend(); }
353
354 //////////////////////////////////////////////////////////////////////////////
355 // Insertion (these are std::multiset like, with no position hints).
356 //
357 // All insertion operations invalidate iterators, pointers and references.
358 // Handles remain valid. Insertion of one element is amortized O(lg size)
359 // (occasional O(size) cost if a new vector allocation is required).
360
insert(const value_type & value)361 const_iterator insert(const value_type& value) { return InsertImpl(value); }
insert(value_type && value)362 const_iterator insert(value_type&& value) {
363 return InsertImpl(std::move_if_noexcept(value));
364 }
365
366 template <class InputIterator>
367 void insert(InputIterator first, InputIterator last);
368
369 template <typename... Args>
370 const_iterator emplace(Args&&... args);
371
372 //////////////////////////////////////////////////////////////////////////////
373 // Removing elements.
374 //
375 // Erasing invalidates all outstanding iterators, pointers and references.
376 // Handles remain valid. Removing one element is amortized O(lg size)
377 // (occasional O(size) cost if a new vector allocation is required).
378 //
379 // Note that it is safe for the element being removed to be in an invalid
380 // state (modified such that it may currently violate the heap property)
381 // when this called.
382
383 // Takes the element from the heap at the given position, erasing that entry
384 // from the heap. This can only be called if |value_type| is movable.
385 value_type take(size_type pos);
386
387 // Version of take that will accept iterators and handles. This can only be
388 // called if |value_type| is movable.
389 template <typename P>
take(P pos)390 value_type take(P pos) {
391 return take(ToIndex(pos));
392 }
393
394 // Takes the top element from the heap.
take_top()395 value_type take_top() { return take(0u); }
396
397 // Erases the element at the given position |pos|.
398 void erase(size_type pos);
399
400 // Version of erase that will accept iterators and handles.
401 template <typename P>
erase(P pos)402 void erase(P pos) {
403 erase(ToIndex(pos));
404 }
405
406 // Removes the element at the top of the heap (accessible via "top", or
407 // "front" or "take").
pop()408 void pop() { erase(0u); }
409
410 // Erases every element that matches the predicate. This is done in-place for
411 // maximum efficiency. Also, to avoid re-entrancy issues, elements are deleted
412 // at the very end.
413 // Note: This function is currently tuned for a use-case where there are
414 // usually 8 or less elements removed at a time. Consider adding a template
415 // parameter if a different tuning is needed.
416 template <typename Functor>
EraseIf(Functor predicate)417 void EraseIf(Functor predicate) {
418 // Stable partition ensures that if no elements are erased, the heap remains
419 // intact.
420 auto erase_start = std::stable_partition(
421 impl_.heap_.begin(), impl_.heap_.end(),
422 [&](const auto& element) { return !predicate(element); });
423
424 // Clear the heap handle of every element that will be erased.
425 for (size_t i = static_cast<size_t>(erase_start - impl_.heap_.begin());
426 i < impl_.heap_.size(); ++i) {
427 ClearHeapHandle(i);
428 }
429
430 // Deleting an element can potentially lead to reentrancy, we move all the
431 // elements to be erased into a temporary container before deleting them.
432 // This is to avoid changing the underlying container during the erase()
433 // call.
434 StackVector<value_type, 8> elements_to_delete;
435 std::move(erase_start, impl_.heap_.end(),
436 std::back_inserter(elements_to_delete.container()));
437
438 impl_.heap_.erase(erase_start, impl_.heap_.end());
439
440 // If no elements were removed, then the heap is still intact.
441 if (elements_to_delete->empty())
442 return;
443
444 // Repair the heap and ensure handles are pointing to the right index.
445 ranges::make_heap(impl_.heap_, value_comp());
446 for (size_t i = 0; i < size(); ++i)
447 SetHeapHandle(i);
448
449 // Explicitly delete elements last.
450 elements_to_delete->clear();
451 }
452
453 //////////////////////////////////////////////////////////////////////////////
454 // Updating.
455 //
456 // Amortized cost of O(lg size).
457
458 // Replaces the element corresponding to |handle| with a new |element|.
Replace(size_type pos,const T & element)459 const_iterator Replace(size_type pos, const T& element) {
460 return ReplaceImpl(pos, element);
461 }
Replace(size_type pos,T && element)462 const_iterator Replace(size_type pos, T&& element) {
463 return ReplaceImpl(pos, std::move_if_noexcept(element));
464 }
465
466 // Versions of Replace that will accept handles and iterators.
467 template <typename P>
Replace(P pos,const T & element)468 const_iterator Replace(P pos, const T& element) {
469 return ReplaceImpl(ToIndex(pos), element);
470 }
471 template <typename P>
Replace(P pos,T && element)472 const_iterator Replace(P pos, T&& element) {
473 return ReplaceImpl(ToIndex(pos), std::move_if_noexcept(element));
474 }
475
476 // Replaces the top element in the heap with the provided element.
ReplaceTop(const T & element)477 const_iterator ReplaceTop(const T& element) {
478 return ReplaceTopImpl(element);
479 }
ReplaceTop(T && element)480 const_iterator ReplaceTop(T&& element) {
481 return ReplaceTopImpl(std::move_if_noexcept(element));
482 }
483
484 // Causes the object at the given location to be resorted into an appropriate
485 // position in the heap. To be used if the object in the heap was externally
486 // modified, and the heap needs to be repaired. This only works if a single
487 // heap element has been modified, otherwise the behaviour is undefined.
488 const_iterator Update(size_type pos);
489 template <typename P>
Update(P pos)490 const_iterator Update(P pos) {
491 return Update(ToIndex(pos));
492 }
493
494 // Applies a modification function to the object at the given location, then
495 // repairs the heap. To be used to modify an element in the heap in-place
496 // while keeping the heap intact.
497 template <typename P, typename UnaryOperation>
Modify(P pos,UnaryOperation unary_op)498 const_iterator Modify(P pos, UnaryOperation unary_op) {
499 size_type index = ToIndex(pos);
500 unary_op(impl_.heap_.at(index));
501 return Update(index);
502 }
503
504 //////////////////////////////////////////////////////////////////////////////
505 // Access to helper functors.
506
value_comp()507 const value_compare& value_comp() const { return impl_.get_value_compare(); }
508
heap_handle_access()509 const heap_handle_accessor& heap_handle_access() const {
510 return impl_.get_heap_handle_access();
511 }
512
513 //////////////////////////////////////////////////////////////////////////////
514 // General operations.
515
516 void swap(IntrusiveHeap& other) noexcept;
swap(IntrusiveHeap & lhs,IntrusiveHeap & rhs)517 friend void swap(IntrusiveHeap& lhs, IntrusiveHeap& rhs) { lhs.swap(rhs); }
518
519 // Comparison operators. These check for exact equality. Two heaps that are
520 // semantically equivalent (contain the same elements, but in different
521 // orders) won't compare as equal using these operators.
522 friend bool operator==(const IntrusiveHeap& lhs, const IntrusiveHeap& rhs) {
523 return lhs.impl_.heap_ == rhs.impl_.heap_;
524 }
525 friend bool operator!=(const IntrusiveHeap& lhs, const IntrusiveHeap& rhs) {
526 return lhs.impl_.heap_ != rhs.impl_.heap_;
527 }
528
529 //////////////////////////////////////////////////////////////////////////////
530 // Utility functions.
531
532 // Converts iterators and handles to indices. Helpers for templated versions
533 // of insert/erase/Replace.
ToIndex(HeapHandle handle)534 size_type ToIndex(HeapHandle handle) { return handle.index(); }
535 size_type ToIndex(const_iterator pos);
536 size_type ToIndex(const_reverse_iterator pos);
537
538 private:
539 // Templated version of ToIndex that lets insert/erase/Replace work with all
540 // integral types.
541 template <typename I, typename = std::enable_if_t<std::is_integral<I>::value>>
ToIndex(I pos)542 size_type ToIndex(I pos) {
543 return static_cast<size_type>(pos);
544 }
545
546 // Returns the last valid index in |heap_|.
GetLastIndex()547 size_type GetLastIndex() const { return impl_.heap_.size() - 1; }
548
549 // Helper functions for setting heap handles.
550 void SetHeapHandle(size_type i);
551 void ClearHeapHandle(size_type i);
552 HeapHandle GetHeapHandle(size_type i);
553
554 // Helpers for doing comparisons between elements inside and outside of the
555 // heap.
556 bool Less(size_type i, size_type j);
557 bool Less(const T& element, size_type i);
558 bool Less(size_type i, const T& element);
559
560 // The following function are all related to the basic heap algorithm
561 // underpinning this data structure. They are templated so that they work with
562 // both movable (U = T&&) and non-movable (U = const T&) types.
563
564 // Primitive helpers for adding removing / elements to the heap. To minimize
565 // moves, the heap is implemented by making a hole where an element used to
566 // be (or where a new element will soon be), and moving the hole around,
567 // before finally filling the hole or deleting the entry corresponding to the
568 // hole.
569 void MakeHole(size_type pos);
570 template <typename U>
571 void FillHole(size_type hole, U element);
572 void MoveHole(size_type new_hole_pos, size_type old_hole_pos);
573
574 // Moves a hold up the tree and fills it with the provided |element|. Returns
575 // the final index of the element.
576 template <typename U>
577 size_type MoveHoleUpAndFill(size_type hole_pos, U element);
578
579 // Moves a hole down the tree and fills it with the provided |element|. If
580 // |kFillWithLeaf| is true it will deterministically move the hole all the
581 // way down the tree, avoiding a second comparison per level, before
582 // potentially moving it back up the tree.
583 struct WithLeafElement {
584 static constexpr bool kIsLeafElement = true;
585 };
586 struct WithElement {
587 static constexpr bool kIsLeafElement = false;
588 };
589 template <typename FillElementType, typename U>
590 size_type MoveHoleDownAndFill(size_type hole_pos, U element);
591
592 // Implementation of Insert and Replace built on top of the MoveHole
593 // primitives.
594 template <typename U>
595 const_iterator InsertImpl(U element);
596 template <typename U>
597 const_iterator ReplaceImpl(size_type pos, U element);
598 template <typename U>
599 const_iterator ReplaceTopImpl(U element);
600
601 // To support comparators that may not be possible to default-construct, we
602 // have to store an instance of value_compare. Using this to store all
603 // internal state of IntrusiveHeap and using private inheritance to store
604 // compare lets us take advantage of an empty base class optimization to avoid
605 // extra space in the common case when Compare has no state.
606 struct Impl : private value_compare, private heap_handle_accessor {
ImplImpl607 Impl(const value_compare& value_comp,
608 const heap_handle_accessor& heap_handle_access)
609 : value_compare(value_comp), heap_handle_accessor(heap_handle_access) {}
610
611 Impl() = default;
612 Impl(Impl&&) = default;
613 Impl(const Impl&) = default;
614 Impl& operator=(Impl&& other) = default;
615 Impl& operator=(const Impl& other) = default;
616
get_value_compareImpl617 const value_compare& get_value_compare() const { return *this; }
get_value_compareImpl618 value_compare& get_value_compare() { return *this; }
619
get_heap_handle_accessImpl620 const heap_handle_accessor& get_heap_handle_access() const { return *this; }
get_heap_handle_accessImpl621 heap_handle_accessor& get_heap_handle_access() { return *this; }
622
623 // The items in the heap.
624 UnderlyingType heap_;
625 } impl_;
626 };
627
628 // Helper class to endow an object with internal HeapHandle storage. By deriving
629 // from this type you endow your class with self-owned storage for a HeapHandle.
630 // This is a move-only type so that the handle follows the element across moves
631 // and resizes of the underlying vector.
632 class BASE_EXPORT InternalHeapHandleStorage {
633 public:
634 InternalHeapHandleStorage();
635 InternalHeapHandleStorage(const InternalHeapHandleStorage&) = delete;
636 InternalHeapHandleStorage(InternalHeapHandleStorage&& other) noexcept;
637 virtual ~InternalHeapHandleStorage();
638
639 InternalHeapHandleStorage& operator=(const InternalHeapHandleStorage&) =
640 delete;
641 InternalHeapHandleStorage& operator=(
642 InternalHeapHandleStorage&& other) noexcept;
643
644 // Allows external clients to get a pointer to the heap handle. This allows
645 // them to remove the element from the heap regardless of its location.
handle()646 HeapHandle* handle() const { return handle_.get(); }
647
648 // Implementation of IntrusiveHeap contract. Inlined to keep heap code as fast
649 // as possible.
SetHeapHandle(HeapHandle handle)650 void SetHeapHandle(HeapHandle handle) {
651 DCHECK(handle.IsValid());
652 if (handle_)
653 *handle_ = handle;
654 }
ClearHeapHandle()655 void ClearHeapHandle() {
656 if (handle_)
657 handle_->reset();
658 }
GetHeapHandle()659 HeapHandle GetHeapHandle() const {
660 if (handle_)
661 return *handle_;
662 return HeapHandle::Invalid();
663 }
664
665 // Utility functions.
666 void swap(InternalHeapHandleStorage& other) noexcept;
swap(InternalHeapHandleStorage & lhs,InternalHeapHandleStorage & rhs)667 friend void swap(InternalHeapHandleStorage& lhs,
668 InternalHeapHandleStorage& rhs) {
669 lhs.swap(rhs);
670 }
671
672 private:
673 std::unique_ptr<HeapHandle> handle_;
674 };
675
676 // Spiritually akin to a std::pair<T, std::unique_ptr<HeapHandle>>. Can be used
677 // to wrap arbitrary types and provide them with a HeapHandle, making them
678 // appropriate for use in an IntrusiveHeap. This is a move-only type.
679 template <typename T>
680 class WithHeapHandle : public InternalHeapHandleStorage {
681 public:
682 WithHeapHandle() = default;
683 // Allow implicit conversion of any type that T supports for ease of use with
684 // InstrusiveHeap constructors/insert/emplace.
685 template <typename U>
WithHeapHandle(U value)686 WithHeapHandle(U value) : value_(std::move_if_noexcept(value)) {}
WithHeapHandle(T && value)687 WithHeapHandle(T&& value) noexcept : value_(std::move(value)) {}
688 // Constructor that forwards all arguments along to |value_|.
689 template <class... Args>
690 explicit WithHeapHandle(Args&&... args);
691 WithHeapHandle(const WithHeapHandle&) = delete;
692 WithHeapHandle(WithHeapHandle&& other) noexcept = default;
693 ~WithHeapHandle() override = default;
694
695 WithHeapHandle& operator=(const WithHeapHandle&) = delete;
696 WithHeapHandle& operator=(WithHeapHandle&& other) = default;
697
value()698 T& value() { return value_; }
value()699 const T& value() const { return value_; }
700
701 // Utility functions.
702 void swap(WithHeapHandle& other) noexcept;
swap(WithHeapHandle & lhs,WithHeapHandle & rhs)703 friend void swap(WithHeapHandle& lhs, WithHeapHandle& rhs) { lhs.swap(rhs); }
704
705 // Comparison operators, for compatibility with ordered STL containers.
706 friend bool operator==(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
707 return lhs.value_ == rhs.value_;
708 }
709 friend bool operator!=(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
710 return lhs.value_ != rhs.value_;
711 }
712 friend bool operator<=(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
713 return lhs.value_ <= rhs.value_;
714 }
715 friend bool operator<(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
716 return lhs.value_ < rhs.value_;
717 }
718 friend bool operator>=(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
719 return lhs.value_ >= rhs.value_;
720 }
721 friend bool operator>(const WithHeapHandle& lhs, const WithHeapHandle& rhs) {
722 return lhs.value_ > rhs.value_;
723 }
724
725 private:
726 T value_;
727 };
728
729 ////////////////////////////////////////////////////////////////////////////////
730 // IMPLEMENTATION DETAILS
731
732 namespace intrusive_heap {
733
ParentIndex(size_t i)734 BASE_EXPORT inline size_t ParentIndex(size_t i) {
735 DCHECK_NE(0u, i);
736 return (i - 1) / 2;
737 }
738
LeftIndex(size_t i)739 BASE_EXPORT inline size_t LeftIndex(size_t i) {
740 return 2 * i + 1;
741 }
742
743 template <typename HandleType>
IsInvalid(const HandleType & handle)744 bool IsInvalid(const HandleType& handle) {
745 return !handle || !handle->IsValid();
746 }
747
CheckInvalidOrEqualTo(HeapHandle handle,size_t index)748 BASE_EXPORT inline void CheckInvalidOrEqualTo(HeapHandle handle, size_t index) {
749 if (handle.IsValid())
750 DCHECK_EQ(index, handle.index());
751 }
752
753 } // namespace intrusive_heap
754
755 ////////////////////////////////////////////////////////////////////////////////
756 // IntrusiveHeap
757
758 template <typename T, typename Compare, typename HeapHandleAccessor>
IntrusiveHeap(const IntrusiveHeap & other)759 IntrusiveHeap<T, Compare, HeapHandleAccessor>::IntrusiveHeap(
760 const IntrusiveHeap& other)
761 : impl_(other.impl_) {
762 for (size_t i = 0; i < size(); ++i) {
763 SetHeapHandle(i);
764 }
765 }
766
767 template <typename T, typename Compare, typename HeapHandleAccessor>
~IntrusiveHeap()768 IntrusiveHeap<T, Compare, HeapHandleAccessor>::~IntrusiveHeap() {
769 clear();
770 }
771
772 template <typename T, typename Compare, typename HeapHandleAccessor>
773 IntrusiveHeap<T, Compare, HeapHandleAccessor>&
774 IntrusiveHeap<T, Compare, HeapHandleAccessor>::operator=(
775 IntrusiveHeap&& other) noexcept {
776 clear();
777 impl_ = std::move(other.impl_);
778 return *this;
779 }
780
781 template <typename T, typename Compare, typename HeapHandleAccessor>
782 IntrusiveHeap<T, Compare, HeapHandleAccessor>&
783 IntrusiveHeap<T, Compare, HeapHandleAccessor>::operator=(
784 const IntrusiveHeap& other) {
785 clear();
786 impl_ = other.impl_;
787 for (size_t i = 0; i < size(); ++i) {
788 SetHeapHandle(i);
789 }
790 return *this;
791 }
792
793 template <typename T, typename Compare, typename HeapHandleAccessor>
794 IntrusiveHeap<T, Compare, HeapHandleAccessor>&
795 IntrusiveHeap<T, Compare, HeapHandleAccessor>::operator=(
796 std::initializer_list<value_type> ilist) {
797 clear();
798 insert(std::begin(ilist), std::end(ilist));
799 }
800
801 template <typename T, typename Compare, typename HeapHandleAccessor>
clear()802 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::clear() {
803 // Make all of the handles invalid before cleaning up the heap.
804 for (size_type i = 0; i < size(); ++i) {
805 ClearHeapHandle(i);
806 }
807
808 // Clear the heap.
809 impl_.heap_.clear();
810 }
811
812 template <typename T, typename Compare, typename HeapHandleAccessor>
813 template <class InputIterator>
insert(InputIterator first,InputIterator last)814 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::insert(InputIterator first,
815 InputIterator last) {
816 for (auto it = first; it != last; ++it) {
817 insert(value_type(*it));
818 }
819 }
820
821 template <typename T, typename Compare, typename HeapHandleAccessor>
822 template <typename... Args>
823 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::const_iterator
emplace(Args &&...args)824 IntrusiveHeap<T, Compare, HeapHandleAccessor>::emplace(Args&&... args) {
825 value_type value(std::forward<Args>(args)...);
826 return InsertImpl(std::move_if_noexcept(value));
827 }
828
829 template <typename T, typename Compare, typename HeapHandleAccessor>
830 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::value_type
take(size_type pos)831 IntrusiveHeap<T, Compare, HeapHandleAccessor>::take(size_type pos) {
832 // Make a hole by taking the element out of the heap.
833 MakeHole(pos);
834 value_type val = std::move(impl_.heap_[pos]);
835
836 // If the element being taken is already the last element then the heap
837 // doesn't need to be repaired.
838 if (pos != GetLastIndex()) {
839 MakeHole(GetLastIndex());
840
841 // Move the hole down the heap, filling it with the current leaf at the
842 // very end of the heap.
843 MoveHoleDownAndFill<WithLeafElement>(
844 pos, std::move(impl_.heap_[GetLastIndex()]));
845 }
846
847 impl_.heap_.pop_back();
848
849 return val;
850 }
851
852 // This is effectively identical to "take", but it avoids an unnecessary move.
853 template <typename T, typename Compare, typename HeapHandleAccessor>
erase(size_type pos)854 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::erase(size_type pos) {
855 DCHECK_LT(pos, size());
856 // Make a hole by taking the element out of the heap.
857 MakeHole(pos);
858
859 // If the element being erased is already the last element then the heap
860 // doesn't need to be repaired.
861 if (pos != GetLastIndex()) {
862 MakeHole(GetLastIndex());
863
864 // Move the hole down the heap, filling it with the current leaf at the
865 // very end of the heap.
866 MoveHoleDownAndFill<WithLeafElement>(
867 pos, std::move_if_noexcept(impl_.heap_[GetLastIndex()]));
868 }
869
870 impl_.heap_.pop_back();
871 }
872
873 template <typename T, typename Compare, typename HeapHandleAccessor>
874 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::const_iterator
Update(size_type pos)875 IntrusiveHeap<T, Compare, HeapHandleAccessor>::Update(size_type pos) {
876 DCHECK_LT(pos, size());
877 MakeHole(pos);
878
879 // Determine if we're >= parent, in which case we may need to go up.
880 bool child_greater_eq_parent = false;
881 size_type i = 0;
882 if (pos > 0) {
883 i = intrusive_heap::ParentIndex(pos);
884 child_greater_eq_parent = !Less(pos, i);
885 }
886
887 if (child_greater_eq_parent) {
888 i = MoveHoleUpAndFill(pos, std::move_if_noexcept(impl_.heap_[pos]));
889 } else {
890 i = MoveHoleDownAndFill<WithElement>(
891 pos, std::move_if_noexcept(impl_.heap_[pos]));
892 }
893
894 return cbegin() + i;
895 }
896
897 template <typename T, typename Compare, typename HeapHandleAccessor>
swap(IntrusiveHeap & other)898 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::swap(
899 IntrusiveHeap& other) noexcept {
900 std::swap(impl_.get_value_compare(), other.impl_.get_value_compare());
901 std::swap(impl_.get_heap_handle_access(),
902 other.impl_.get_heap_handle_access());
903 std::swap(impl_.heap_, other.impl_.heap_);
904 }
905
906 template <typename T, typename Compare, typename HeapHandleAccessor>
907 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::size_type
ToIndex(const_iterator pos)908 IntrusiveHeap<T, Compare, HeapHandleAccessor>::ToIndex(const_iterator pos) {
909 DCHECK(cbegin() <= pos);
910 DCHECK(pos <= cend());
911 if (pos == cend())
912 return HeapHandle::kInvalidIndex;
913 return pos - cbegin();
914 }
915
916 template <typename T, typename Compare, typename HeapHandleAccessor>
917 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::size_type
ToIndex(const_reverse_iterator pos)918 IntrusiveHeap<T, Compare, HeapHandleAccessor>::ToIndex(
919 const_reverse_iterator pos) {
920 DCHECK(crbegin() <= pos);
921 DCHECK(pos <= crend());
922 if (pos == crend())
923 return HeapHandle::kInvalidIndex;
924 return (pos.base() - cbegin()) - 1;
925 }
926
927 template <typename T, typename Compare, typename HeapHandleAccessor>
SetHeapHandle(size_type i)928 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::SetHeapHandle(size_type i) {
929 impl_.get_heap_handle_access().SetHeapHandle(&impl_.heap_[i], HeapHandle(i));
930 intrusive_heap::CheckInvalidOrEqualTo(GetHeapHandle(i), i);
931 }
932
933 template <typename T, typename Compare, typename HeapHandleAccessor>
ClearHeapHandle(size_type i)934 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::ClearHeapHandle(
935 size_type i) {
936 impl_.get_heap_handle_access().ClearHeapHandle(&impl_.heap_[i]);
937 DCHECK(!GetHeapHandle(i).IsValid());
938 }
939
940 template <typename T, typename Compare, typename HeapHandleAccessor>
GetHeapHandle(size_type i)941 HeapHandle IntrusiveHeap<T, Compare, HeapHandleAccessor>::GetHeapHandle(
942 size_type i) {
943 return impl_.get_heap_handle_access().GetHeapHandle(&impl_.heap_[i]);
944 }
945
946 template <typename T, typename Compare, typename HeapHandleAccessor>
Less(size_type i,size_type j)947 bool IntrusiveHeap<T, Compare, HeapHandleAccessor>::Less(size_type i,
948 size_type j) {
949 DCHECK_LT(i, size());
950 DCHECK_LT(j, size());
951 return impl_.get_value_compare()(impl_.heap_[i], impl_.heap_[j]);
952 }
953
954 template <typename T, typename Compare, typename HeapHandleAccessor>
Less(const T & element,size_type i)955 bool IntrusiveHeap<T, Compare, HeapHandleAccessor>::Less(const T& element,
956 size_type i) {
957 DCHECK_LT(i, size());
958 return impl_.get_value_compare()(element, impl_.heap_[i]);
959 }
960
961 template <typename T, typename Compare, typename HeapHandleAccessor>
Less(size_type i,const T & element)962 bool IntrusiveHeap<T, Compare, HeapHandleAccessor>::Less(size_type i,
963 const T& element) {
964 DCHECK_LT(i, size());
965 return impl_.get_value_compare()(impl_.heap_[i], element);
966 }
967
968 template <typename T, typename Compare, typename HeapHandleAccessor>
MakeHole(size_type pos)969 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::MakeHole(size_type pos) {
970 DCHECK_LT(pos, size());
971 ClearHeapHandle(pos);
972 }
973
974 template <typename T, typename Compare, typename HeapHandleAccessor>
975 template <typename U>
FillHole(size_type hole_pos,U element)976 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::FillHole(size_type hole_pos,
977 U element) {
978 // The hole that we're filling may not yet exist. This can occur when
979 // inserting a new element into the heap.
980 DCHECK_LE(hole_pos, size());
981 if (hole_pos == size()) {
982 impl_.heap_.push_back(std::move_if_noexcept(element));
983 } else {
984 impl_.heap_[hole_pos] = std::move_if_noexcept(element);
985 }
986 SetHeapHandle(hole_pos);
987 }
988
989 template <typename T, typename Compare, typename HeapHandleAccessor>
MoveHole(size_type new_hole_pos,size_type old_hole_pos)990 void IntrusiveHeap<T, Compare, HeapHandleAccessor>::MoveHole(
991 size_type new_hole_pos,
992 size_type old_hole_pos) {
993 // The old hole position may be one past the end. This occurs when a new
994 // element is being added.
995 DCHECK_NE(new_hole_pos, old_hole_pos);
996 DCHECK_LT(new_hole_pos, size());
997 DCHECK_LE(old_hole_pos, size());
998
999 if (old_hole_pos == size()) {
1000 impl_.heap_.push_back(std::move_if_noexcept(impl_.heap_[new_hole_pos]));
1001 } else {
1002 impl_.heap_[old_hole_pos] =
1003 std::move_if_noexcept(impl_.heap_[new_hole_pos]);
1004 }
1005 SetHeapHandle(old_hole_pos);
1006 }
1007
1008 template <typename T, typename Compare, typename HeapHandleAccessor>
1009 template <typename U>
1010 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::size_type
MoveHoleUpAndFill(size_type hole_pos,U element)1011 IntrusiveHeap<T, Compare, HeapHandleAccessor>::MoveHoleUpAndFill(
1012 size_type hole_pos,
1013 U element) {
1014 // Moving 1 spot beyond the end is fine. This happens when we insert a new
1015 // element.
1016 DCHECK_LE(hole_pos, size());
1017
1018 // Stop when the element is as far up as it can go.
1019 while (hole_pos != 0) {
1020 // If our parent is >= to us, we can stop.
1021 size_type parent = intrusive_heap::ParentIndex(hole_pos);
1022 if (!Less(parent, element))
1023 break;
1024
1025 MoveHole(parent, hole_pos);
1026 hole_pos = parent;
1027 }
1028
1029 FillHole(hole_pos, std::move_if_noexcept(element));
1030 return hole_pos;
1031 }
1032
1033 template <typename T, typename Compare, typename HeapHandleAccessor>
1034 template <typename FillElementType, typename U>
1035 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::size_type
MoveHoleDownAndFill(size_type hole_pos,U element)1036 IntrusiveHeap<T, Compare, HeapHandleAccessor>::MoveHoleDownAndFill(
1037 size_type hole_pos,
1038 U element) {
1039 DCHECK_LT(hole_pos, size());
1040
1041 // If we're filling with a leaf, then that leaf element is about to be erased.
1042 // We pretend that the space doesn't exist in the heap.
1043 const size_type n = size() - (FillElementType::kIsLeafElement ? 1 : 0);
1044
1045 DCHECK_LT(hole_pos, n);
1046 DCHECK(!GetHeapHandle(hole_pos).IsValid());
1047
1048 while (true) {
1049 // If this spot has no children, then we've gone down as far as we can go.
1050 size_type left = intrusive_heap::LeftIndex(hole_pos);
1051 if (left >= n)
1052 break;
1053 size_type right = left + 1;
1054
1055 // Get the larger of the potentially two child nodes.
1056 size_type largest = left;
1057 if (right < n && Less(left, right))
1058 largest = right;
1059
1060 // If we're not deterministically moving the element all the way down to
1061 // become a leaf, then stop when it is >= the largest of the children.
1062 if (!FillElementType::kIsLeafElement && !Less(element, largest))
1063 break;
1064
1065 MoveHole(largest, hole_pos);
1066 hole_pos = largest;
1067 }
1068
1069 if (FillElementType::kIsLeafElement) {
1070 // If we're filling with a leaf node we may need to bubble the leaf back up
1071 // the tree a bit to repair the heap.
1072 hole_pos = MoveHoleUpAndFill(hole_pos, std::move_if_noexcept(element));
1073 } else {
1074 FillHole(hole_pos, std::move_if_noexcept(element));
1075 }
1076 return hole_pos;
1077 }
1078
1079 template <typename T, typename Compare, typename HeapHandleAccessor>
1080 template <typename U>
1081 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::const_iterator
InsertImpl(U element)1082 IntrusiveHeap<T, Compare, HeapHandleAccessor>::InsertImpl(U element) {
1083 // MoveHoleUpAndFill can tolerate the initial hole being in a slot that
1084 // doesn't yet exist. It will be created by MoveHole by copy/move, thus
1085 // removing the need for a default constructor.
1086 size_type i = MoveHoleUpAndFill(size(), std::move_if_noexcept(element));
1087 return cbegin() + static_cast<difference_type>(i);
1088 }
1089
1090 template <typename T, typename Compare, typename HeapHandleAccessor>
1091 template <typename U>
1092 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::const_iterator
ReplaceImpl(size_type pos,U element)1093 IntrusiveHeap<T, Compare, HeapHandleAccessor>::ReplaceImpl(size_type pos,
1094 U element) {
1095 // If we're greater than our parent we need to go up, otherwise we may need
1096 // to go down.
1097 MakeHole(pos);
1098 size_type i = 0;
1099 if (!Less(element, pos)) {
1100 i = MoveHoleUpAndFill(pos, std::move_if_noexcept(element));
1101 } else {
1102 i = MoveHoleDownAndFill<WithElement>(pos, std::move_if_noexcept(element));
1103 }
1104 return cbegin() + static_cast<difference_type>(i);
1105 }
1106
1107 template <typename T, typename Compare, typename HeapHandleAccessor>
1108 template <typename U>
1109 typename IntrusiveHeap<T, Compare, HeapHandleAccessor>::const_iterator
ReplaceTopImpl(U element)1110 IntrusiveHeap<T, Compare, HeapHandleAccessor>::ReplaceTopImpl(U element) {
1111 MakeHole(0u);
1112 size_type i =
1113 MoveHoleDownAndFill<WithElement>(0u, std::move_if_noexcept(element));
1114 return cbegin() + static_cast<difference_type>(i);
1115 }
1116
1117 ////////////////////////////////////////////////////////////////////////////////
1118 // WithHeapHandle
1119
1120 template <typename T>
1121 template <class... Args>
WithHeapHandle(Args &&...args)1122 WithHeapHandle<T>::WithHeapHandle(Args&&... args)
1123 : value_(std::forward<Args>(args)...) {}
1124
1125 template <typename T>
swap(WithHeapHandle & other)1126 void WithHeapHandle<T>::swap(WithHeapHandle& other) noexcept {
1127 InternalHeapHandleStorage::swap(other);
1128 std::swap(value_, other.value_);
1129 }
1130
1131 } // namespace base
1132
1133 #endif // BASE_CONTAINERS_INTRUSIVE_HEAP_H_
1134