• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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