| Index: third_party/WebKit/Source/platform/scheduler/base/intrusive_heap.h
|
| diff --git a/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap.h b/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..635df1f0394b614096760f1b46e80e265d844669
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap.h
|
| @@ -0,0 +1,224 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
|
| +#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
|
| +
|
| +#include <algorithm>
|
| +#include <vector>
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +namespace blink {
|
| +namespace scheduler {
|
| +
|
| +template <typename T>
|
| +class IntrusiveHeap;
|
| +
|
| +// Intended as an opaque wrapper around |index_|.
|
| +class HeapHandle {
|
| + public:
|
| + HeapHandle() : index_(0u) {}
|
| +
|
| + bool IsValid() const { return index_ != 0u; }
|
| +
|
| + private:
|
| + template <typename T>
|
| + friend class IntrusiveHeap;
|
| +
|
| + HeapHandle(size_t index) : index_(index) {}
|
| +
|
| + size_t index_;
|
| +};
|
| +
|
| +// A standard min-heap with the following assumptions:
|
| +// 1. T has operator <=
|
| +// 2. T has method void SetHeapHandle(HeapHandle handle)
|
| +// 3. T has method void ClearHeapHandle()
|
| +// 4. T is moveable
|
| +// 5. T is default constructible
|
| +// 6. The heap size never gets terribly big so reclaiming memory on pop/erase
|
| +// isn't a priority.
|
| +//
|
| +// The reason IntrusiveHeap exists is to provide similar performance to
|
| +// std::priority_queue while allowing removal of arbitrary elements.
|
| +template <typename T>
|
| +class IntrusiveHeap {
|
| + public:
|
| + IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {}
|
| +
|
| + ~IntrusiveHeap() {
|
| + for (size_t i = 1; i <= size_; i++) {
|
| + MakeHole(i);
|
| + }
|
| + }
|
| +
|
| + bool empty() const { return size_ == 0; }
|
| +
|
| + size_t size() const { return size_; }
|
| +
|
| + void clear() {
|
| + for (size_t i = 1; i <= size_; i++) {
|
| + MakeHole(i);
|
| + }
|
| + nodes_.resize(kMinimumHeapSize);
|
| + size_ = 0;
|
| + }
|
| +
|
| + const T& min() const {
|
| + DCHECK_GE(size_, 1u);
|
| + return nodes_[1];
|
| + }
|
| +
|
| + void pop() {
|
| + DCHECK_GE(size_, 1u);
|
| + MakeHole(1u);
|
| + size_t top_index = size_--;
|
| + if (!empty())
|
| + MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index]));
|
| + }
|
| +
|
| + void insert(T&& element) {
|
| + size_++;
|
| + if (size_ >= nodes_.size())
|
| + nodes_.resize(nodes_.size() * 2);
|
| + // Notionally we have a hole in the tree at index |size_|, move this up
|
| + // to find the right insertion point.
|
| + MoveHoleUpAndFillWithElement(size_, std::move(element));
|
| + }
|
| +
|
| + void erase(HeapHandle handle) {
|
| + DCHECK_GT(handle.index_, 0u);
|
| + DCHECK_LE(handle.index_, size_);
|
| + MakeHole(handle.index_);
|
| + size_t top_index = size_--;
|
| + if (empty() || top_index == handle.index_)
|
| + return;
|
| + if (nodes_[handle.index_] <= nodes_[top_index]) {
|
| + MoveHoleDownAndFillWithLeafElement(handle.index_,
|
| + std::move(nodes_[top_index]));
|
| + } else {
|
| + MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index]));
|
| + }
|
| + }
|
| +
|
| + void ReplaceMin(T&& element) {
|
| + // Note |element| might not be a leaf node so we can't use
|
| + // MoveHoleDownAndFillWithLeafElement.
|
| + MoveHoleDownAndFillWithElement(1u, std::move(element));
|
| + }
|
| +
|
| + void ChangeKey(HeapHandle handle, T&& element) {
|
| + if (nodes_[handle.index_] <= element) {
|
| + MoveHoleDownAndFillWithLeafElement(handle.index_, std::move(element));
|
| + } else {
|
| + MoveHoleUpAndFillWithElement(handle.index_, std::move(element));
|
| + }
|
| + }
|
| +
|
| + // Caution mutating the heap invalidates the iterators.
|
| + const T* begin() const { return &nodes_[1u]; }
|
| + const T* end() const { return begin() + size_; }
|
| +
|
| + private:
|
| + enum {
|
| + // The majority of sets in the scheduler have 0-3 items in them (a few will
|
| + // have perhaps up to 100), so this means we usually only have to allocate
|
| + // memory once.
|
| + kMinimumHeapSize = 4u
|
| + };
|
| +
|
| + size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) {
|
| + DCHECK_GT(new_hole_pos, 0u);
|
| + DCHECK_LE(new_hole_pos, size_);
|
| + DCHECK_GT(new_hole_pos, 0u);
|
| + DCHECK_LE(new_hole_pos, size_);
|
| + DCHECK_NE(old_hole_pos, new_hole_pos);
|
| + nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]);
|
| + nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos));
|
| + return new_hole_pos;
|
| + }
|
| +
|
| + // Notionally creates a hole in the tree at |index|.
|
| + void MakeHole(size_t index) {
|
| + DCHECK_GT(index, 0u);
|
| + DCHECK_LE(index, size_);
|
| + nodes_[index].ClearHeapHandle();
|
| + }
|
| +
|
| + void FillHole(size_t hole, T&& element) {
|
| + DCHECK_GT(hole, 0u);
|
| + DCHECK_LE(hole, size_);
|
| + nodes_[hole] = std::move(element);
|
| + nodes_[hole].SetHeapHandle(HeapHandle(hole));
|
| + DCHECK(std::is_heap(begin(), end(), CompareNodes));
|
| + }
|
| +
|
| + static bool CompareNodes(const T& a, const T& b) { return b <= a; }
|
| +
|
| + // Moves the |hole| up the tree and when the right position has been found
|
| + // |element| is moved in.
|
| + void MoveHoleUpAndFillWithElement(size_t hole, T&& element) {
|
| + DCHECK_GT(hole, 0u);
|
| + DCHECK_LE(hole, size_);
|
| + while (hole >= 2u) {
|
| + size_t parent_pos = hole / 2;
|
| + if (nodes_[parent_pos] <= element)
|
| + break;
|
| +
|
| + hole = MoveHole(parent_pos, hole);
|
| + }
|
| + FillHole(hole, std::move(element));
|
| + }
|
| +
|
| + // Moves the |hole| down the tree and when the right position has been found
|
| + // |element| is moved in.
|
| + void MoveHoleDownAndFillWithElement(size_t hole, T&& element) {
|
| + DCHECK_GT(hole, 0u);
|
| + DCHECK_LE(hole, size_);
|
| + size_t child_pos = hole * 2;
|
| + while (child_pos < size_) {
|
| + if (nodes_[child_pos + 1] <= nodes_[child_pos])
|
| + child_pos++;
|
| +
|
| + if (element <= nodes_[child_pos])
|
| + break;
|
| +
|
| + hole = MoveHole(child_pos, hole);
|
| + child_pos *= 2;
|
| + }
|
| + if (child_pos == size_ && !(element <= nodes_[child_pos]))
|
| + hole = MoveHole(child_pos, hole);
|
| + FillHole(hole, std::move(element));
|
| + }
|
| +
|
| + // Moves the |hole| down the tree and when the right position has been found
|
| + // |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement
|
| + // (it does one key comparison per level instead of two) but only valid for
|
| + // leaf elements (i.e. one of the max values).
|
| + void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) {
|
| + DCHECK_GT(hole, 0u);
|
| + DCHECK_LE(hole, size_);
|
| + size_t child_pos = hole * 2;
|
| + while (child_pos < size_) {
|
| + size_t second_child = child_pos + 1;
|
| + if (nodes_[second_child] <= nodes_[child_pos])
|
| + child_pos = second_child;
|
| +
|
| + hole = MoveHole(child_pos, hole);
|
| + child_pos *= 2;
|
| + }
|
| + if (child_pos == size_)
|
| + hole = MoveHole(child_pos, hole);
|
| + MoveHoleUpAndFillWithElement(hole, std::move(leaf_element));
|
| + }
|
| +
|
| + std::vector<T> nodes_; // NOTE we use 1-based indexing
|
| + size_t size_;
|
| +};
|
| +
|
| +} // namespace scheduler
|
| +} // namespace blink
|
| +
|
| +#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
|
|
|