blob: 8a15f0108b4595b2aad5617f9a8ac145aef9e6db [file] [log] [blame] [edit]
// Copyright 2016 The Fuchsia 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 FBL_INTRUSIVE_WAVL_TREE_H_
#define FBL_INTRUSIVE_WAVL_TREE_H_
#include <zircon/assert.h>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/intrusive_container_node_utils.h>
#include <fbl/intrusive_container_utils.h>
#include <fbl/intrusive_pointer_traits.h>
#include <fbl/intrusive_wavl_tree_internal.h>
// Implementation Notes:
//
// WAVLTree<> is an implementation of a "Weak AVL" tree; a self
// balancing binary search tree whose rebalancing algorithm was
// originally described in
//
// Bernhard Haeupler, Siddhartha Sen, and Robert E. Tarjan. 2015.
// Rank-Balanced Trees. ACM Trans. Algorithms 11, 4, Article 30 (June 2015), 26 pages.
// DOI=http://dx.doi.org/10.1145/2689412
//
// See also
// https://en.wikipedia.org/wiki/WAVL_tree
// http://sidsen.azurewebsites.net/papers/rb-trees-talg.pdf
//
// WAVLTree<>s, like HashTables, are associative containers and support all of
// the same key-centric operations (such as find() and insert_or_find()) that
// HashTables support.
//
// Additionally, WAVLTree's are internally ordered by key (unlike HashTables
// which are un-ordered). Iteration forwards or backwards runs in amortized
// constant time, but in O(log) time in an individual worst case. Forward
// iteration will enumerate the elements in monotonically increasing order (as
// defined by the KeyTraits::LessThan operation).
//
// Two additional operations are supported because of the ordered nature of a
// WAVLTree:
// upper_bound(key) : Finds the element (E) in the tree such that E.key > key
// lower_bound(key) : Finds the element (E) in the tree such that E.key >= key
//
// The worst depth of a WAVL tree depends on whether or not the tree has ever
// been subject to erase operations.
// ++ If the tree has seen only insert operations, the worst case depth of the
// tree is log_phi(N), where phi is the golden ratio. This is the same bound
// as that of an AVL tree.
// ++ If the tree has seen erase operations in addition to insert operations,
// the worst case depth of the tree is 2*log_2(N). This is the same bound as
// a Red-Black tree.
//
// Insertion runs in O(log) time; finding the location takes O(log) time while
// post-insert rebalancing runs in amortized constant time.
//
// Erase-by-key runs in O(log) time; finding the node to erase takes O(log) time
// while post-erase rebalancing runs in amortized constant time.
//
// Because of the intrusive nature of the container, direct-erase operations
// (AKA, erase operations where the reference to the element to be erased is
// already known) run in amortized constant time.
//
namespace fbl {
namespace tests {
namespace intrusive_containers {
class WAVLTreeChecker;
class WAVLBalanceTestObserver;
} // namespace intrusive_containers
} // namespace tests
template <typename PtrType_, NodeOptions Options, typename RankType>
struct WAVLTreeNodeStateBase
: public internal::CommonNodeStateBase<WAVLTreeNodeStateBase<PtrType_, Options, RankType>> {
private:
using Base = internal::CommonNodeStateBase<WAVLTreeNodeStateBase<PtrType_, Options, RankType>>;
public:
using PtrType = PtrType_;
using PtrTraits = internal::ContainerPtrTraits<PtrType_>;
static constexpr NodeOptions kNodeOptions = Options;
WAVLTreeNodeStateBase() = default;
~WAVLTreeNodeStateBase() {
ZX_DEBUG_ASSERT(IsValid());
if constexpr (!(kNodeOptions & fbl::NodeOptions::AllowClearUnsafe)) {
ZX_DEBUG_ASSERT(!InContainer());
}
}
bool IsValid() const { return (parent_ || (!parent_ && !left_ && !right_)); }
bool InContainer() const { return (parent_ != nullptr); }
// Defer to CommonNodeStateBase for enforcement of the various copy/move
// rules. Make sure, however, that we explicitly do not allow our own default
// construction/assignment operators change anything about our state.
WAVLTreeNodeStateBase(const WAVLTreeNodeStateBase& other) : Base(other) {}
WAVLTreeNodeStateBase& operator=(const WAVLTreeNodeStateBase& other) {
this->Base::operator=(other);
return *this;
}
WAVLTreeNodeStateBase(WAVLTreeNodeStateBase&& other) : Base(std::move(other)) {}
WAVLTreeNodeStateBase& operator=(WAVLTreeNodeStateBase&& other) {
this->Base::operator=(std::move(other));
return *this;
}
protected:
template <typename, typename, typename, typename, typename, typename>
friend class WAVLTree;
friend class tests::intrusive_containers::WAVLTreeChecker;
friend class tests::intrusive_containers::WAVLBalanceTestObserver;
typename PtrTraits::RawPtrType parent_ = nullptr;
typename PtrTraits::RawPtrType left_ = nullptr;
typename PtrTraits::RawPtrType right_ = nullptr;
RankType rank_{};
};
template <typename PtrType_, NodeOptions Options>
struct WAVLTreeNodeState<PtrType_, Options, DefaultWAVLTreeRankType>
: public WAVLTreeNodeStateBase<PtrType_, Options, DefaultWAVLTreeRankType> {
WAVLTreeNodeState() = default;
// Defer to WAVLTreeNodeStateBase for enforcement of the various copy/move rules.
WAVLTreeNodeState(const WAVLTreeNodeState&) = default;
WAVLTreeNodeState& operator=(const WAVLTreeNodeState&) = default;
WAVLTreeNodeState(WAVLTreeNodeState&&) = default;
WAVLTreeNodeState& operator=(WAVLTreeNodeState&&) = default;
bool rank_parity() const { return this->rank_; }
void promote_rank() { this->rank_ = !this->rank_; }
void double_promote_rank() {}
void demote_rank() { this->rank_ = !this->rank_; }
void double_demote_rank() {}
};
template <typename PtrType, NodeOptions Options, typename TagType>
struct WAVLTreeContainable;
template <typename PtrType_, typename TagType_>
struct DefaultWAVLTreeTraits {
private:
using ValueType = typename internal::ContainerPtrTraits<PtrType_>::ValueType;
public:
using PtrType = PtrType_;
using TagType = TagType_;
using PtrTraits = internal::ContainerPtrTraits<PtrType_>;
static auto& node_state(typename PtrTraits::RefType obj) {
if constexpr (std::is_same_v<TagType, DefaultObjectTag>) {
return obj.ValueType::wavl_node_state_;
} else {
return obj.template GetContainableByTag<TagType>().wavl_node_state_;
}
}
using NodeState =
std::decay_t<std::invoke_result_t<decltype(node_state), typename PtrTraits::RefType>>;
};
template <typename PtrType_, NodeOptions Options = NodeOptions::None,
typename TagType_ = DefaultObjectTag>
struct WAVLTreeContainable {
public:
using PtrType = PtrType_;
using TagType = TagType_;
bool InContainer() const {
using Node = WAVLTreeContainable<PtrType, Options, TagType>;
return Node::wavl_node_state_.InContainer();
}
private:
friend DefaultWAVLTreeTraits<PtrType, TagType>;
WAVLTreeNodeState<PtrType, Options, DefaultWAVLTreeRankType> wavl_node_state_;
};
template <typename KeyType_, typename PtrType_,
typename KeyTraits_ = DefaultKeyedObjectTraits<
KeyType_, typename internal::ContainerPtrTraits<PtrType_>::ValueType>,
typename TagType_ = DefaultObjectTag,
typename NodeTraits_ = DefaultWAVLTreeTraits<PtrType_, TagType_>,
typename Observer_ = tests::intrusive_containers::DefaultWAVLTreeObserver>
class __POINTER(KeyType_) WAVLTree {
private:
// Private fwd decls of the iterator implementation.
template <typename IterTraits>
class iterator_impl;
struct iterator_traits;
struct const_iterator_traits;
public:
// Aliases used to reduce verbosity and expose types/traits to tests
using KeyType = KeyType_;
using PtrType = PtrType_;
using KeyTraits = KeyTraits_;
using TagType = TagType_;
using NodeTraits = NodeTraits_;
using Observer = Observer_;
using PtrTraits = internal::ContainerPtrTraits<PtrType>;
using RawPtrType = typename PtrTraits::RawPtrType;
using ValueType = typename PtrTraits::ValueType;
using RefType = typename PtrTraits::RefType;
using ContainerType = WAVLTree<KeyType_, PtrType_, KeyTraits_, TagType_, NodeTraits_, Observer_>;
using CheckerType = ::fbl::tests::intrusive_containers::WAVLTreeChecker;
// Declarations of the standard iterator types.
using iterator = iterator_impl<iterator_traits>;
using const_iterator = iterator_impl<const_iterator_traits>;
// WAVL Trees support amortized constant erase. Technically, the worst case
// for any individual erase operation involves O(log) demotions, followed by
// a double rotation operation. Given D total erase operations, however,
// the maximum number of operations (demotions + rotations) is 2*D, given
// the amortized constant erase time.
//
static constexpr bool SupportsConstantOrderErase = true;
static constexpr bool SupportsConstantOrderSize = true;
static constexpr bool IsAssociative = true;
static constexpr bool IsSequenced = false;
// Default construction gives an empty tree.
constexpr WAVLTree() noexcept {
using NodeState = internal::node_state_t<NodeTraits, RefType>;
// Make certain that the type of pointer we are expected to manage matches
// the type of pointer that our Node type expects to manage.
static_assert(std::is_same_v<PtrType, typename NodeState::PtrType>,
"WAVLTree's pointer type must match its Node's pointerType");
// WAVLTree does not currently support direct remove-from-container.
static_assert(!(NodeState::kNodeOptions & NodeOptions::AllowRemoveFromContainer),
"WAVLTree does not support nodes which allow RemoveFromContainer.");
}
// Rvalue construction is permitted, but will result in the move of the tree
// contents from one instance of the list to the other (even for unmanaged
// pointers)
WAVLTree(WAVLTree&& other_tree) noexcept : WAVLTree() { swap(other_tree); }
// Rvalue assignment is permitted for managed trees, and when the target is
// an empty tree of unmanaged pointers. Like Rvalue construction, it will
// result in the move of the source contents to the destination.
WAVLTree& operator=(WAVLTree&& other_tree) {
ZX_DEBUG_ASSERT(PtrTraits::IsManaged || is_empty());
clear();
swap(other_tree);
return *this;
}
~WAVLTree() {
// It is considered an error to allow a tree of unmanaged pointers to
// destruct of there are still elements in it. Managed pointer trees
// will automatically release their references to their elements.
ZX_DEBUG_ASSERT(PtrTraits::IsManaged || is_empty());
clear();
}
// Standard begin/end, cbegin/cend iterator accessors.
iterator begin() { return iterator(left_most_); }
const_iterator begin() const { return const_iterator(left_most_); }
const_iterator cbegin() const { return const_iterator(left_most_); }
iterator end() { return iterator(sentinel()); }
const_iterator end() const { return const_iterator(sentinel()); }
const_iterator cend() const { return const_iterator(sentinel()); }
// Iterator accessors to the root node.
iterator root() { return is_empty() ? end() : iterator(root_); }
const_iterator croot() const { return is_empty() ? cend() : const_iterator(root_); }
// make_iterator : construct an iterator out of a pointer to an object
iterator make_iterator(ValueType& obj) { return iterator(&obj); }
const_iterator make_iterator(const ValueType& obj) const {
return const_iterator(&const_cast<ValueType&>(obj));
}
// is_empty : True if the tree has at least one element in it, false otherwise.
bool is_empty() const { return root_ == nullptr; }
// front
//
// Return a reference to the element at the front of the list without
// removing it. It is an error to call front on an empty list.
typename PtrTraits::RefType front() {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(left_most_));
return *left_most_;
}
typename PtrTraits::ConstRefType front() const {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(left_most_));
return *left_most_;
}
// back
//
// Return a reference to the element at the back of the list without
// removing it. It is an error to call back on an empty list.
typename PtrTraits::RefType back() {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(right_most_));
return *right_most_;
}
typename PtrTraits::ConstRefType back() const {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(right_most_));
return *right_most_;
}
void insert(const PtrType& ptr) { insert(PtrType(ptr)); }
void insert(PtrType&& ptr) { internal_insert(ptr); }
// insert or find
//
// Insert the object pointed to by ptr if it is not already in the
// tree, or find the object that the ptr collided with instead.
//
// @param ptr A pointer to the new object to insert.
// @param iter An optional out parameter pointer to an iterator which
// will reference either the newly inserted item, or the item whose key
// collided with ptr.
//
// @return true if there was no collision and the item was successfully
// inserted. false otherwise.
//
bool insert_or_find(const PtrType& ptr, iterator* iter = nullptr) {
return insert_or_find(PtrType(ptr), iter);
}
bool insert_or_find(PtrType&& ptr, iterator* iter = nullptr) {
RawPtrType obj = PtrTraits::GetRaw(ptr);
RawPtrType collision = nullptr;
internal_insert(ptr, &collision);
if (collision) {
Observer::RecordInsertCollision(obj, iterator(collision));
}
if (iter) {
*iter = collision ? iterator(collision) : iterator(obj);
}
return !collision;
}
// insert_or_replace
//
// Find the element in the tree with the same key as *ptr and replace
// it with ptr, then return the pointer to the element which was replaced.
// If no element in the tree shares a key with *ptr, simply add ptr to
// the tree and return nullptr.
//
PtrType insert_or_replace(const PtrType& ptr) { return insert_or_replace(PtrType(ptr)); }
PtrType insert_or_replace(PtrType&& ptr) {
ZX_DEBUG_ASSERT(ptr != nullptr);
ZX_DEBUG_ASSERT(!NodeTraits::node_state(*ptr).InContainer());
RawPtrType collision = nullptr;
internal_insert(ptr, &collision);
// If there was a collision, swap our node with the node we collided
// with.
if (collision) {
ZX_DEBUG_ASSERT(ptr != nullptr);
Observer::RecordInsertReplace(iterator(collision), PtrTraits::GetRaw(ptr));
return internal_swap(collision, std::move(ptr));
}
return nullptr;
}
// pop_front and pop_back
//
// Removes either the left-most or right-most member of tree and transfers
// the pointer to the caller. If the list is empty, return a nullptr
// instance of PtrType.
PtrType pop_front() { return internal_erase(left_most_); }
PtrType pop_back() { return internal_erase(right_most_); }
// find
//
// Find the first node in the tree whose key matches "key" and return an
// iterator to it. Return end() if no node in the tree has a key which
// matches "key".
const_iterator find(const KeyType& key) const {
RawPtrType node = root_;
while (internal::valid_sentinel_ptr(node)) {
auto node_key = KeyTraits::GetKey(*node);
if (KeyTraits::EqualTo(key, node_key))
return const_iterator(node);
auto& ns = NodeTraits::node_state(*node);
node = KeyTraits::LessThan(key, node_key) ? ns.left_ : ns.right_;
}
return end();
}
iterator find(const KeyType& key) {
const_iterator citer = const_cast<const ContainerType*>(this)->find(key);
return iterator(citer.node_);
}
// upper_bound
//
// Find the first node in the tree whose key is strictly greater than the
// caller provided key. Returns end() if no such node exists.
const_iterator upper_bound(const KeyType& key) const {
return internal_upper_lower_bound<UpperBoundTraits>(key);
}
iterator upper_bound(const KeyType& key) {
const_iterator citer = const_cast<const ContainerType*>(this)->upper_bound(key);
return iterator(citer.node_);
}
// lower_bound
//
// Find the first node in the tree whose key is greater than or equal to the
// caller provided key. Returns end() if no such node exists.
const_iterator lower_bound(const KeyType& key) const {
return internal_upper_lower_bound<LowerBoundTraits>(key);
}
iterator lower_bound(const KeyType& key) {
const_iterator citer = const_cast<const ContainerType*>(this)->lower_bound(key);
return iterator(citer.node_);
}
// erase
//
// Remove the first element in the tree whose key matches "key" and return a
// pointer the removed object. Return a nullptr instance of PtrType if no
// such element exists in the tree.
PtrType erase(const KeyType& key) { return erase(find(key)); }
// erase (direct)
//
// Remove the object directly referenced either by "iter" or "obj" from the
// tree and return a pointer to it. In the case of an iterator based erase,
// return a nullptr instance of PtrType if the iterator is invalid. It is
// an error to either use a valid iterator from a different tree instance,
// or to attempt to remove an element which is not currently a member of
// this tree instance.
PtrType erase(const iterator& iter) {
if (!iter.IsValid())
return PtrType(nullptr);
return internal_erase(&(*iter));
}
PtrType erase(ValueType& obj) { return internal_erase(&obj); }
// clear
//
// Clear out the tree, unlinking all of the elements in the process. For
// managed pointer types, this will release all references held by the tree
// to the objects which were in it.
void clear() {
if (is_empty())
return;
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(root_));
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(left_most_));
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(right_most_));
// Clear the left and right sentinels right now so that we don't have
// to worry about special casing them while cleaning up the tree.
NodeTraits::node_state(*left_most_).left_ = nullptr;
NodeTraits::node_state(*right_most_).right_ = nullptr;
RawPtrType owner = sentinel();
RawPtrType* link_ptr = &root_;
while (true) {
auto& ns = NodeTraits::node_state(**link_ptr);
if ((ns.left_ == nullptr) && (ns.right_ == nullptr)) {
// Leaf node. Trim it. Start by reclaiming the pointer
// reference (if this is a managed pointer type) and holding
// onto it until the end of this scope. Note, we need to flag
// this temp copy of the reference as UNUSED in case the pointer
// type is unmanaged.
ZX_DEBUG_ASSERT(ns.parent_ == owner);
__UNUSED PtrType leaf = PtrTraits::Reclaim(*link_ptr);
// This leaf node node now has no parent, and the pointer which
// pointed to us is now null.
ns.parent_ = nullptr;
*link_ptr = nullptr;
// If we are removing the root, and it is a leaf node, then we
// are done.
if (link_ptr == &root_)
break;
// Now climb back up the tree.
link_ptr = &GetLinkPtrToNode(owner);
owner = NodeTraits::node_state(*owner).parent_;
} else {
// Non-leaf node, descend. We have already detached the left
// and right sentinels, so we shouldn't be seeing any here.
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(ns.left_));
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(ns.right_));
owner = *link_ptr;
link_ptr = (ns.left_ != nullptr) ? &ns.left_ : &ns.right_;
}
}
ZX_DEBUG_ASSERT(root_ == nullptr);
left_most_ = sentinel();
right_most_ = sentinel();
count_ = 0;
}
// clear_unsafe
//
// See comments in fbl/intrusive_single_list.h
// Think carefully before calling this!
void clear_unsafe() {
static_assert(PtrTraits::IsManaged == false,
"clear_unsafe is not allowed for containers of managed pointers");
static_assert(NodeTraits::NodeState::kNodeOptions & NodeOptions::AllowClearUnsafe,
"Container does not support clear_unsafe. Consider adding "
"NodeOptions::AllowClearUnsafe to your node storage.");
root_ = nullptr;
left_most_ = sentinel();
right_most_ = sentinel();
count_ = 0;
}
// swap : swaps the contents of two trees.
void swap(WAVLTree& other) {
internal::Swap(root_, other.root_);
internal::Swap(left_most_, other.left_most_);
internal::Swap(right_most_, other.right_most_);
internal::Swap(count_, other.count_);
// Fix up the sentinel values.
FixSentinelsAfterSwap();
other.FixSentinelsAfterSwap();
}
// size : return the current number of elements in the tree.
size_t size() const { return count_; }
// erase_if
//
// Find the first member of the list which satisfies the predicate given by
// 'fn' and erase it from the list, returning a referenced pointer to the
// removed element. Return nullptr if no element satisfies the predicate.
template <typename UnaryFn>
PtrType erase_if(UnaryFn fn) {
for (auto iter = begin(); iter != end(); ++iter) {
if (fn(*iter))
return erase(iter);
}
return PtrType(nullptr);
}
// find_if
//
// Find the first member of the list which satisfies the predicate given by
// 'fn' and return a const& to the PtrType in the list which refers to it.
// Return nullptr if no member satisfies the predicate.
template <typename UnaryFn>
const_iterator find_if(UnaryFn fn) const {
for (auto iter = begin(); iter != end(); ++iter)
if (fn(*iter))
return const_iterator(iter.node_);
return const_iterator(sentinel());
}
template <typename UnaryFn>
iterator find_if(UnaryFn fn) {
const_iterator citer = const_cast<const ContainerType*>(this)->find_if(fn);
return iterator(citer.node_);
}
private:
// The traits of a non-const iterator
struct iterator_traits {
using RefType = typename PtrTraits::RefType;
using RawPtrType = typename PtrTraits::RawPtrType;
};
// The traits of a const iterator
struct const_iterator_traits {
using RefType = typename PtrTraits::ConstRefType;
using RawPtrType = typename PtrTraits::ConstRawPtrType;
};
// Trait classes used to help implement symmetric operations.
//
// Notes about notation:
// + LR denotes Left for the Forward version of the operation and Right
// for the Reverse version.
// + RL denotes Right for the Forward version of the operation and Left
// for the Reverse version.
//
// Examples...
// Forward : LR-child of node X == the left child of node X.
// Reverse : LR-child of node X == the right child of node X.
//
// Forward : RL-most node of the tree == the right most node of the tree
// Reverse : RL-most node of the tree == the left most node of the tree
struct ReverseTraits; // fwd decl
struct ForwardTraits {
using Inverse = ReverseTraits;
template <typename NodeState>
static RawPtrType& LRChild(NodeState& ns) {
return ns.left_;
}
template <typename NodeState>
static RawPtrType& RLChild(NodeState& ns) {
return ns.right_;
}
static RawPtrType& LRMost(ContainerType& tree) { return tree.left_most_; }
static RawPtrType& RLMost(ContainerType& tree) { return tree.right_most_; }
};
struct ReverseTraits {
using Inverse = ForwardTraits;
template <typename NodeState>
static RawPtrType& LRChild(NodeState& ns) {
return ns.right_;
}
template <typename NodeState>
static RawPtrType& RLChild(NodeState& ns) {
return ns.left_;
}
static RawPtrType& LRMost(ContainerType& tree) { return tree.right_most_; }
static RawPtrType& RLMost(ContainerType& tree) { return tree.left_most_; }
};
// Trait classes used to define the bound condition for upper_bound and
// lower_bound.
struct UpperBoundTraits {
static bool GoRight(const KeyType& key, const KeyType& node_key) {
return KeyTraits::EqualTo(node_key, key) || KeyTraits::LessThan(node_key, key);
}
};
struct LowerBoundTraits {
static bool GoRight(const KeyType& key, const KeyType& node_key) {
return KeyTraits::LessThan(node_key, key);
}
};
// The shared implementation of the iterator
template <class IterTraits>
class iterator_impl {
public:
iterator_impl() {}
iterator_impl(const iterator_impl& other) { node_ = other.node_; }
iterator_impl& operator=(const iterator_impl& other) {
node_ = other.node_;
return *this;
}
bool IsValid() const { return internal::valid_sentinel_ptr(node_); }
explicit operator bool() const { return IsValid(); }
bool operator==(const iterator_impl& other) const { return node_ == other.node_; }
bool operator!=(const iterator_impl& other) const { return node_ != other.node_; }
// Prefix
iterator_impl& operator++() {
if (IsValid())
advance<ForwardTraits>();
return *this;
}
iterator_impl& operator--() {
// If this was a default constructed iterator, then it cannot
// back up.
if (node_ != nullptr) {
// If we are at the end() of the tree, then recover the tree
// pointer from the sentinel and back up to the right-most node.
if (internal::is_sentinel_ptr(node_)) {
node_ = internal::unmake_sentinel<ContainerType*>(node_)->right_most_;
} else {
advance<ReverseTraits>();
}
}
return *this;
}
// Postfix
iterator_impl operator++(int) {
iterator_impl ret(*this);
++(*this);
return ret;
}
iterator_impl operator--(int) {
iterator_impl ret(*this);
--(*this);
return ret;
}
iterator_impl parent() const {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node_));
auto& ns = NodeTraits::node_state(*node_);
return iterator_impl(ns.parent_);
}
iterator_impl left() const {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node_));
auto& ns = NodeTraits::node_state(*node_);
return iterator_impl(ns.left_);
}
iterator_impl right() const {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node_));
auto& ns = NodeTraits::node_state(*node_);
return iterator_impl(ns.right_);
}
typename PtrTraits::PtrType CopyPointer() const {
return IsValid() ? PtrTraits::Copy(node_) : nullptr;
}
typename IterTraits::RefType operator*() const {
ZX_DEBUG_ASSERT(node_);
return *node_;
}
typename IterTraits::RawPtrType operator->() const {
ZX_DEBUG_ASSERT(node_);
return node_;
}
private:
friend ContainerType;
iterator_impl(typename PtrTraits::RawPtrType node) : node_(node) {}
template <typename LRTraits>
void advance() {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node_));
// Find the next node in the ordered sequence.
// key. This will be either...
// 1) The RL-most child of our LR-hand sub-tree.
// 2) Our first ancestor for which we are a LR-hand descendant.
auto ns = &NodeTraits::node_state(*node_);
auto rl_child = LRTraits::RLChild(*ns);
if (rl_child != nullptr) {
node_ = rl_child;
// The RL-hand child of the RL-most node is terminated
// using the sentinel value for this tree instead of nullptr.
// Have we hit it? If so, then we are done.
if (internal::is_sentinel_ptr(node_))
return;
// While we can go LR, do so.
auto lr_child = LRTraits::LRChild(NodeTraits::node_state(*node_));
while (lr_child != nullptr) {
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(lr_child));
node_ = lr_child;
lr_child = LRTraits::LRChild(NodeTraits::node_state(*node_));
}
} else {
// Climb up the tree until we traverse a LR-hand link. Because
// of the sentinel termination, we should never attempt to climb
// up past the root.
bool done;
auto ns = &NodeTraits::node_state(*node_);
do {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(ns->parent_));
auto parent_ns = &NodeTraits::node_state(*ns->parent_);
done = (LRTraits::LRChild(*parent_ns) == node_);
ZX_DEBUG_ASSERT(done || (LRTraits::RLChild(*parent_ns) == node_));
node_ = ns->parent_;
ns = parent_ns;
} while (!done);
}
}
typename PtrTraits::RawPtrType node_ = nullptr;
}; // class iterator_impl
// The test framework's 'checker' class is our friend.
friend CheckerType;
// move semantics only
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(WAVLTree);
void internal_insert(PtrType& ptr, RawPtrType* collision = nullptr) {
ZX_DEBUG_ASSERT(ptr != nullptr);
auto& ns = NodeTraits::node_state(*ptr);
ZX_DEBUG_ASSERT(ns.IsValid() && !ns.InContainer());
// The rank of an inserted node always starts at 0.
ns.rank_ = 0;
// If the tree is currently empty, then this is easy.
if (root_ == nullptr) {
ns.parent_ = sentinel();
ns.left_ = sentinel();
ns.right_ = sentinel();
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(left_most_) &&
internal::is_sentinel_ptr(right_most_));
left_most_ = PtrTraits::GetRaw(ptr);
right_most_ = PtrTraits::GetRaw(ptr);
root_ = PtrTraits::Leak(ptr);
++count_;
Observer::RecordInsert(root());
return;
}
// Find the proper position for this node.
auto key = KeyTraits::GetKey(*ptr);
bool is_left_most = true;
bool is_right_most = true;
RawPtrType parent = root_;
RawPtrType* owner;
while (true) {
auto parent_key = KeyTraits::GetKey(*parent);
// Looks like we collided with an object in the collection which has
// the same key as the object being inserted. This is only allowed
// during an insert_or_find operation (collision is non-null).
// Assert this in debug builds. If this is an insert_or_find
// operation, fill out the collision [out] parameter so the called
// knows which object he/she collided with. Either way, do not
// actually insert the object.
if (KeyTraits::EqualTo(key, parent_key)) {
ZX_DEBUG_ASSERT(collision && *collision == nullptr);
*collision = parent;
return;
}
// Update user-defined invariants in the subtree node only when the
// key does not collide.
Observer::RecordInsertTraverse(PtrTraits::GetRaw(ptr), iterator(parent));
auto& parent_ns = NodeTraits::node_state(*parent);
// Decide which side of the current parent-under-consideration the
// node to be inserted belongs on. If we are going left, then we
// are no longer right-most, and vice-versa.
if (KeyTraits::LessThan(key, parent_key)) {
owner = &parent_ns.left_;
is_right_most = false;
} else {
owner = &parent_ns.right_;
is_left_most = false;
}
// If we would have run out of valid pointers in the direction we
// should be searching, then we are done.
if (!internal::valid_sentinel_ptr(*owner))
break;
// We belong on a side of the parent-under-consideration which
// already has a child. Move down to the child and consider it
// instead.
parent = *owner;
}
// We know that we are not the root of the tree, therefore we cannot be
// both left and right-most.
ZX_DEBUG_ASSERT(!is_left_most || !is_right_most);
if (is_right_most) {
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(*owner));
ns.right_ = sentinel();
right_most_ = PtrTraits::GetRaw(ptr);
} else if (is_left_most) {
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(*owner));
ns.left_ = sentinel();
left_most_ = PtrTraits::GetRaw(ptr);
}
// The owner link must either be nullptr or the sentinel. This is
// equivalent to saying that the pointer is not valid (using the pointer
// traits definition of "valid").
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(*owner) == false);
ns.parent_ = parent;
*owner = PtrTraits::Leak(ptr);
++count_;
Observer::RecordInsert(iterator(*owner));
// Finally, perform post-insert balance operations.
BalancePostInsert(*owner);
}
PtrType internal_erase(RawPtrType ptr) {
if (!internal::valid_sentinel_ptr(ptr))
return PtrType(nullptr);
auto& ns = NodeTraits::node_state(*ptr);
RawPtrType* owner = &GetLinkPtrToNode(ptr);
ZX_DEBUG_ASSERT(*owner == ptr);
// If the node we want to remove has two children, swap it with the
// left-most node of the right-hand sub-tree before proceeding. This
// will guarantee that we are operating in a 0 or 1 child case.
if (internal::valid_sentinel_ptr(ns.left_) && internal::valid_sentinel_ptr(ns.right_)) {
RawPtrType* new_owner = &ns.right_;
auto new_ns = &NodeTraits::node_state(*ns.right_);
while (new_ns->left_ != nullptr) {
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(new_ns->left_));
new_owner = &new_ns->left_;
new_ns = &NodeTraits::node_state(*new_ns->left_);
}
owner = SwapWithRightDescendant(*owner, *new_owner);
ZX_DEBUG_ASSERT(*owner == ptr);
}
// Now that we know our relationship with our parent, go ahead and start
// the process of removing the node. Keep track of the target node's
// parent, whether it was it's parent's left or right child, and whether
// it was a 1-child or a 2-child. We will need this info when it comes
// time to rebalance.
RawPtrType parent = ns.parent_;
bool was_one_child, was_left_child;
ZX_DEBUG_ASSERT(parent != nullptr);
if (!internal::is_sentinel_ptr(parent)) {
auto& parent_ns = NodeTraits::node_state(*parent);
was_one_child = ns.rank_parity() != parent_ns.rank_parity();
was_left_child = &parent_ns.left_ == owner;
} else {
was_one_child = false;
was_left_child = false;
}
// Reclaim our refernce from our owner and unlink. We return the to the
// caller when we are done.
PtrType removed = PtrTraits::Reclaim(*owner);
*owner = nullptr;
// We know that we have at most one child. If we do have a child,
// promote it to our position. While we are handling the cases,
// maintain the LR-most bookkeeping. Consider the 1 and 0 child cases
// separately.
//
// 1-child case:
// We are promoting the LR-child. If we were RL-most, then our RL-child
// is the sentinel value. We need to transfer this sentinel value to
// the RL-most node in our LR-subtree. We also need to update the
// parent pointer of our LR-child to point to the removed node's parent.
//
// 0-child case:
// We are not promoting any node, but we may or may not have been the
// LR-most.
//
// ++ If the target is both the left and right-most node in the tree, it
// is a leaf, then the target *must* be the final node in the tree. The
// tree's left and right-most need to be updated to be the sentinel
// value, and the sentinels need to be cleared from the target node's
// state structure.
// ++ If the target is the LR-most node in the tree and a leaf, then its
// parent is now the LR-most node in the tree. We need to transfer
// the sentinel from the target's LR-child to the pointer which used
// to point to it, and update the LR-most bookkeeping in the tree to
// point to the target's parent. The target node's RL-child should
// already be nullptr.
// ++ If the target neither the left nor the right most node in the
// tree, then nothing special needs to happen with regard to the
// left/right-most bookkeeping.
RawPtrType target = PtrTraits::GetRaw(removed);
if (internal::valid_sentinel_ptr(ns.left_)) {
PromoteLRChild<ForwardTraits>(*owner, target);
} else if (internal::valid_sentinel_ptr(ns.right_)) {
PromoteLRChild<ReverseTraits>(*owner, target);
} else {
// The target's LR-child is the sentinel if and only if the target
// is the LR-most node in the tree.
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(ns.left_) == (left_most_ == target));
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(ns.right_) == (right_most_ == target));
if (internal::is_sentinel_ptr(ns.left_)) {
if (internal::is_sentinel_ptr(ns.right_)) {
// Target is both left and right most.
ZX_DEBUG_ASSERT(count_ == 1);
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(ns.parent_));
left_most_ = sentinel();
right_most_ = sentinel();
ns.left_ = nullptr;
ns.right_ = nullptr;
} else {
// Target is just left most.
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(ns.parent_));
ZX_DEBUG_ASSERT(ns.right_ == nullptr);
left_most_ = ns.parent_;
*owner = ns.left_;
ns.left_ = nullptr;
}
} else if (internal::is_sentinel_ptr(ns.right_)) {
// Target is just right most.
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(ns.parent_));
ZX_DEBUG_ASSERT(ns.left_ == nullptr);
right_most_ = ns.parent_;
*owner = ns.right_;
ns.right_ = nullptr;
}
// Disconnect the target's parent pointer and we should be done.
ns.parent_ = nullptr;
}
// At this point in time, the target node should have been completely
// removed from the tree. Its internal state should be valid, and
// indicate that it is not in the container.
ZX_DEBUG_ASSERT(ns.IsValid() && !ns.InContainer());
// Update the count bookkeeping.
--count_;
Observer::RecordErase(target, iterator(parent));
// Time to rebalance. We know that we don't need to rebalance if we
// just removed the root (IOW - its parent was the sentinel value).
if (!internal::is_sentinel_ptr(parent)) {
if (was_one_child) {
// If the node we removed was a 1-child, then we may have just
// turned its parent into a 2,2 leaf node. If so, we have a
// leaf node with non-zero rank and need to fix the problem.
BalancePostErase_Fix22Leaf(parent);
} else {
// If the node we removed was a 2-child, the we may have just
// created a 3 child which we need to fix. The balance routine
// will handle checking for us, but it will need to know whether
// the node we removed was its parent's left or right child in
// order to un-ambiguously perform the check.
if (was_left_child)
BalancePostErase_FixLR3Child<ForwardTraits>(parent);
else
BalancePostErase_FixLR3Child<ReverseTraits>(parent);
}
}
// Give the reference to the node we just removed back to the caller.
return removed;
}
// internal::Swap a node which is currently in the tree with a new node.
//
// old_node *must* already be in the tree.
// new_node *must* not be in the tree.
// old_node and new_node *must* have the same key.
//
PtrType internal_swap(RawPtrType old_node, PtrType&& new_node) {
ZX_DEBUG_ASSERT(old_node != nullptr);
ZX_DEBUG_ASSERT(new_node != nullptr);
ZX_DEBUG_ASSERT(KeyTraits::EqualTo(KeyTraits::GetKey(*old_node), KeyTraits::GetKey(*new_node)));
auto& old_ns = NodeTraits::node_state(*old_node);
auto& new_ns = NodeTraits::node_state(*new_node);
auto new_raw = PtrTraits::GetRaw(new_node);
ZX_DEBUG_ASSERT(old_ns.InContainer());
ZX_DEBUG_ASSERT(!new_ns.InContainer());
// Start with the left child state.
if (internal::valid_sentinel_ptr(old_ns.left_)) {
// Fix the left-child's parent pointer.
NodeTraits::node_state(*old_ns.left_).parent_ = new_raw;
} else {
// We have no left child, so there is no left-child parent pointer
// to fixup, but we may need to fix the left-most bookkeeping.
if (internal::is_sentinel_ptr(old_ns.left_)) {
ZX_DEBUG_ASSERT(left_most_ == old_node);
left_most_ = new_raw;
}
}
new_ns.left_ = old_ns.left_;
old_ns.left_ = nullptr;
// Same routine, but this time with the right child state.
if (internal::valid_sentinel_ptr(old_ns.right_)) {
// Fix the right-child's parent pointer.
NodeTraits::node_state(*old_ns.right_).parent_ = new_raw;
} else {
// We have no right child, so there is no right-child parent pointer
// to fixup, but we may need to fix the right-most bookkeeping.
if (internal::is_sentinel_ptr(old_ns.right_)) {
ZX_DEBUG_ASSERT(right_most_ == old_node);
right_most_ = new_raw;
}
}
new_ns.right_ = old_ns.right_;
old_ns.right_ = nullptr;
// Don't forget transfer the rank bookkeeping from the old node to the
// new node.
new_ns.rank_ = old_ns.rank_;
// Finally, update the pointer which pointed to the old node to point to
// the new node, taking ownership of the managed reference (if any) in
// the process. The update the parent pointers, and finally reclaim the
// reference from the node we just swapped out and give it back to the
// caller.
GetLinkPtrToNode(old_node) = PtrTraits::Leak(new_node);
new_ns.parent_ = old_ns.parent_;
old_ns.parent_ = nullptr;
return PtrTraits::Reclaim(old_node);
}
template <typename BoundTraits>
const_iterator internal_upper_lower_bound(const KeyType& key) const {
RawPtrType node = root_;
RawPtrType found = sentinel();
while (internal::valid_sentinel_ptr(node)) {
auto node_key = KeyTraits::GetKey(*node);
if (BoundTraits::GoRight(key, node_key)) {
// If we need to look for a larger node value (key > node_key in
// the case of lower_bound, key >= node_key in the case of
// upper_bound), then go right. If we cannot go right, then
// there is other element in this container whose key satisfies
// the bound condition. Break out of the loop and return the
// best node we have found so far.
auto& ns = NodeTraits::node_state(*node);
if (ns.right_ == nullptr)
break;
node = ns.right_;
} else {
// If this node's key must be greater than or equal to the
// user's key. This node is now our candidate for our found
// node.
found = node;
// If this node has a left-hand child, it is possible that there
// is a better bound somewhere underneath it. Set the node
// pointer to the root of the left hand sub-tree and keep
// looking.
node = NodeTraits::node_state(*node).left_;
}
}
return const_iterator(found);
}
constexpr RawPtrType sentinel() const { return internal::make_sentinel<RawPtrType>(this); }
// Swaps the positions of two nodes, one of which is guaranteed to be a
// right-hand descendant of the other.
//
// @note Node #1 is the ancestor node while node #2 is the descendant node.
//
// @param ptr_ref1 A reference to the pointer which points to node #1.
// @param ptr_ref2 A reference to the pointer which points to node #2.
// @return The new pointer to the pointer which points to node #1.
//
RawPtrType* SwapWithRightDescendant(RawPtrType& ptr_ref1, RawPtrType& ptr_ref2) {
RawPtrType node1 = ptr_ref1;
RawPtrType node2 = ptr_ref2;
auto& ns1 = NodeTraits::node_state(*node1);
auto& ns2 = NodeTraits::node_state(*node2);
auto ns1_lp = internal::valid_sentinel_ptr(ns1.left_)
? &NodeTraits::node_state(*ns1.left_).parent_
: nullptr;
auto ns2_lp = internal::valid_sentinel_ptr(ns2.left_)
? &NodeTraits::node_state(*ns2.left_).parent_
: nullptr;
auto ns2_rp = internal::valid_sentinel_ptr(ns2.right_)
? &NodeTraits::node_state(*ns2.right_).parent_
: nullptr;
// node 2 is a right-hand descendant of node 1, so node 1's right hand
// pointer must be valid.
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(ns1.right_));
auto ns1_rp = &NodeTraits::node_state(*ns1.right_).parent_;
// Start by updating the LR-most bookkeeping. Node 1 cannot be the
// right-most node, and node 2 cannot be the left most node (because we
// know that node 2 is to the right of node 1).
//
// If node 1 is currently the left-most, then node 2 will be when we are
// done. Likewise, if node 2 is currently the right-most, then node 1
// will be the right-most when we are done.
if (node1 == left_most_)
left_most_ = node2;
if (node2 == right_most_)
right_most_ = node1;
// Next, swap the core state of node 1 and node 2.
internal::Swap(ns1.parent_, ns2.parent_);
internal::Swap(ns1.left_, ns2.left_);
internal::Swap(ns1.right_, ns2.right_);
internal::Swap(ns1.rank_, ns2.rank_);
// At this point, there are two scenarios.
//
// Case #1
// Node 2 was an indirect descendant of node 1. In this case, all we
// need to do is swap the ptr_ref[12] pointers and fix-up the various
// children's parent pointers (when they exist).
//
// Case #2
// Node 2 was the direct descendant of node 1; in this case the right
// hand child. In this case, we know 2 things...
//
// 1) node1.right is the same pointer as ptr_ref2
// 2) node2.parent is the same pointer as node 1's right-child's parent.
//
// Because of this (and because of the swapping of core state prior to
// this), we know...
//
// 1) node1.parent currently points to node 1, but should point to node 2.
// 2) node2.right currently points to node 2, but should point to node 1.
// 3) ptr_ref1 still points to node 1, but should point to node 2.
// 4) node2.parent (aka; ns1_rp) currently points to node 1's old
// parent (which is correct).
//
// We fix issues 1-3 by swapping ptr_ref1 and node2.right, and by
// directly setting setting node1.parent to node2.
//
// Finally, we need to return the new pointer to the pointer which
// points to node 1. In case #1, this is just ptr_ref2. In case #2,
// however, this has become node 2's right hand pointer.
//
// Perform the common child fixup first, then deal with the special
// cases.
if (ns1_lp)
*ns1_lp = node2;
if (ns2_lp)
*ns2_lp = node1;
if (ns2_rp)
*ns2_rp = node1;
if (&ptr_ref2 != &ns1.right_) {
// Case #1.
internal::Swap(ptr_ref1, ptr_ref2);
*ns1_rp = node2;
return &ptr_ref2;
} else {
ZX_DEBUG_ASSERT(ns1.parent_ == node1);
ZX_DEBUG_ASSERT(ns2.right_ == node2);
internal::Swap(ptr_ref1, ns2.right_);
ns1.parent_ = node2;
return &ns2.right_;
}
}
// Promote the LR-child of a node to be removed into the node's position.
// Update the LR-node bookkeeping in the process. The LRTraits will
// determine if we are promoting the left or right child (the forward
// operation promotes left).
//
// Requirements:
// ++ The node being removed *must* have an LR-child.
// ++ The node being removed *must not* have an RL-child.
// ++ The owner reference *must* have already been disconnected from the
// node to be removed.
//
// @param owner A reference to the pointer which points to the node to be
// removed.
// @param node A pointer to the node to be removed.
//
template <typename LRTraits>
void PromoteLRChild(RawPtrType& owner, RawPtrType node) {
ZX_DEBUG_ASSERT(owner == nullptr);
ZX_DEBUG_ASSERT(node != nullptr);
auto& ns = NodeTraits::node_state(*node);
RawPtrType& lr_child = LRTraits::LRChild(ns);
RawPtrType& rl_child = LRTraits::RLChild(ns);
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(lr_child) &&
!internal::valid_sentinel_ptr(rl_child));
// Promote by transferring the LR-Child pointer to the owner pointer and
// fixing up the LR-Child's parent pointer be the current parent of the
// node to be removed.
owner = lr_child; // owner now points to the promoted node.
lr_child = nullptr; // the node being removed no longer has any lr child.
NodeTraits::node_state(*owner).parent_ = ns.parent_;
// The removed node is the RL-most node if (and only if) its RL-child
// was the sentinel value.
RawPtrType& rl_most = LRTraits::RLMost(*this);
ZX_DEBUG_ASSERT((rl_most == node) == internal::is_sentinel_ptr(rl_child));
if (internal::is_sentinel_ptr(rl_child)) {
// The target node was the RL-most. Find the new RL-most node. It will
// be the RL-most node in the LR-subtree of the target node. Once
// found, update the RL-child of the new RL-most node to be the
// sentinel value.
RawPtrType replacement = owner;
RawPtrType* next_rl_child;
while (true) {
auto& replacement_ns = NodeTraits::node_state(*replacement);
next_rl_child = &LRTraits::RLChild(replacement_ns);
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(*next_rl_child));
if (*next_rl_child == nullptr)
break;
replacement = *next_rl_child;
}
// Update the bookkeeping.
rl_most = replacement;
*next_rl_child = sentinel();
rl_child = nullptr;
}
// Unlink the parent pointer for the target node and we should be done.
// The left and right children of the target node should already be
// nullptr by now.
ns.parent_ = nullptr;
ZX_DEBUG_ASSERT(ns.left_ == nullptr);
ZX_DEBUG_ASSERT(ns.right_ == nullptr);
}
// After we have swapped contents with another tree, we need to fix up the
// sentinel values so that they refer to the proper tree. Otherwise tree
// A's sentinels will point at tree B's, and vice-versa.
void FixSentinelsAfterSwap() {
if (root_) {
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(root_));
ZX_DEBUG_ASSERT(left_most_ && !internal::is_sentinel_ptr(left_most_));
ZX_DEBUG_ASSERT(right_most_ && !internal::is_sentinel_ptr(right_most_));
auto& root_ns = NodeTraits::node_state(*root_);
auto& left_most_ns = NodeTraits::node_state(*left_most_);
auto& right_most_ns = NodeTraits::node_state(*right_most_);
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(left_most_ns.left_));
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(right_most_ns.right_));
root_ns.parent_ = sentinel();
left_most_ns.left_ = sentinel();
right_most_ns.right_ = sentinel();
} else {
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(left_most_));
ZX_DEBUG_ASSERT(internal::is_sentinel_ptr(right_most_));
left_most_ = sentinel();
right_most_ = sentinel();
}
}
// GetLinkPtrToNode.
//
// Obtain a reference to the pointer which points to node. The will either be
// a reference to the node's parent's left child, right child, or the root
// node of the tree if the child has no parent.
RawPtrType& GetLinkPtrToNode(RawPtrType node) {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node));
auto& ns = NodeTraits::node_state(*node);
if (internal::is_sentinel_ptr(ns.parent_)) {
ZX_DEBUG_ASSERT(ns.parent_ == sentinel());
ZX_DEBUG_ASSERT(root_ == node);
return root_;
}
ZX_DEBUG_ASSERT(ns.parent_ != nullptr);
auto& parent_ns = NodeTraits::node_state(*ns.parent_);
if (parent_ns.left_ == node)
return parent_ns.left_;
ZX_DEBUG_ASSERT(parent_ns.right_ == node);
return parent_ns.right_;
}
// RotateLR<LRTraits>
//
// Perform a Rotate-LR operation at 'node'.
//
// Let...
// X = node
// Y = LR-Child of X (if any)
// Z = X's parent.
//
// A Rotate-LR operation at 'node' is defined as follows.
// 1) Z becomes the LR-Child of X
// 2) Y becomes the RL-Child of Z
// 3) X takes Z's position in the tree
//
// If [XYZ]_link is the pointer which points to [XYZ], then we can describe
// the operation as the following permutation of the links.
//
// link | before | swap1 | swap2 |
// --------+--------+-------+-------+
// X_link | X | Y | Y +
// Y_link | Y | X | Z +
// Z_link | Z | Z | X +
//
// Note that link's are bi-directional. We need to update the parent
// pointers as well. Let G be Z's parent at the start of the operation.
// The permutation should be.
//
// node | P(n) before | P(n) after |
// --------+-------------+------------+
// X | Z | G |
// Y | X | Z |
// Z | G | X |
//
// We should not need to worry about the LR-most-ness of any of the nodes.
// Reasoning is as follows...
//
// 1) Z might be the LR-most node in the tree, but only if it has no
// LR-Child. It cannot be the RL-most node in the tree, because we know
// that it has an RL-child (X). When we rotate, Z moves to the LR. So
// if it or one of its LR-children was LR-most, they remain that way.
// 2) X might be the RL-most node in the tree, but only if it has no
// RL-child. It cannot be the LR-most node in the tree because it is the
// RL-child of Z. When we rotate, X moves up in the tree following Z's
// RL-child link. This means that if X was on the RL-edge of the tree
// (implying that it or one of its children was RL-most), it remains
// there, preserving the RL-most-ness of it or its children.
// 3) Before the rotation, Y cannot be either the RL-most node in the tree
// (it is X's LR-child), or the LR-most node in the tree (it's parent, X,
// is Z's RL-child). After the rotation, Y still cannot be either
// LR-most (it is now Z's RL-child) or RL-most (it's parent, Z, is X's
// LR-child).
template <typename LRTraits>
void RotateLR(RawPtrType node, RawPtrType parent) {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node)); // Node must be valid
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(parent)); // Node must have a parent
// Aliases, just to make the code below match the notation used above.
RawPtrType X = node;
RawPtrType Z = parent;
auto& X_ns = NodeTraits::node_state(*X);
auto& Z_ns = NodeTraits::node_state(*Z);
// X must be the RL-child of Z.
ZX_DEBUG_ASSERT(LRTraits::RLChild(Z_ns) == X);
RawPtrType& X_link = LRTraits::RLChild(Z_ns);
RawPtrType& Y_link = LRTraits::LRChild(X_ns);
RawPtrType& Z_link = GetLinkPtrToNode(Z);
RawPtrType G = Z_ns.parent_;
RawPtrType Y = Y_link;
// The pointer to Y cannot be a sentinel, because that would imply that
// X was LR-most.
ZX_DEBUG_ASSERT(!internal::is_sentinel_ptr(Y));
// Record the rotation observation before the links are updated. The children are specified here
// as a left rotation. These are transformed by the LRTraits for the right rotation case.
Observer::RecordRotation(iterator(X), iterator(Y), iterator(LRTraits::RLChild(X_ns)),
iterator(Z), iterator(LRTraits::LRChild(Z_ns)));
// Permute the downstream links.
RawPtrType tmp = X_link;
X_link = Y_link;
Y_link = Z_link;
Z_link = tmp;
// Update the parent pointers (note that Y may not exist).
X_ns.parent_ = G;
Z_ns.parent_ = X;
if (Y) {
NodeTraits::node_state(*Y).parent_ = Z;
}
}
// PostInsertFixupLR<LRTraits>
//
// Called after the promotion stage of an insert operation, when node's
// parent has become 0,2 node and the rank rule needs to be restored.
//
// If node is the LR-child of parent, define...
// X = node
// Y = RL-child of X
// Z = parent
//
// There are two possibilities of what to do next.
//
// if (Y is null) or (Y is a 2-child)
// then...
// 1) rotate-RL about X
// 2) demote Z
//
// OR
//
// if (Y is a 1-child)
// then...
// 1) rotate-LR about Y
// 2) rotate-RL about Y
// 3) promote Y
// 4) demote X
// 5) demote Z
template <typename LRTraits>
void PostInsertFixupLR(RawPtrType node, RawPtrType parent) {
using RLTraits = typename LRTraits::Inverse;
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node));
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(parent));
auto& node_ns = NodeTraits::node_state(*node);
auto& parent_ns = NodeTraits::node_state(*parent);
ZX_DEBUG_ASSERT(LRTraits::LRChild(parent_ns) == node);
RawPtrType rl_child = LRTraits::RLChild(node_ns);
auto rl_child_ns =
internal::valid_sentinel_ptr(rl_child) ? &NodeTraits::node_state(*rl_child) : nullptr;
if (!rl_child_ns || (rl_child_ns->rank_parity() == node_ns.rank_parity())) {
// Case #1; single rotation
//
// Start by performing a Rotate-RL at node
RotateLR<RLTraits>(node, parent);
Observer::RecordInsertRotation();
// Now demote node's old parent (now its LR-child)
parent_ns.demote_rank();
} else {
// Case #2; double rotation.
//
// Start by performing a Rotate-LR at rl_child. Afterwards,
// rl_child will be LR-child of parent (and node is now its
// LR-child). Rotate-RL at rl_child.
RotateLR<LRTraits>(rl_child, node);
RotateLR<RLTraits>(rl_child, parent);
Observer::RecordInsertDoubleRotation();
// 1 promotion and 2 demotions.
rl_child_ns->promote_rank();
node_ns.demote_rank();
parent_ns.demote_rank();
}
}
// BalancePostInsert
//
// Execute the bottom-up post-insert rebalancing algorithm.
//
// @param node A pointer to the node which was just inserted.
//
void BalancePostInsert(RawPtrType node) {
// We do not balance the tree after inserting the first (root)
// node, so we should be able to assert that we have a valid parent.
auto node_ns = &NodeTraits::node_state(*node);
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node_ns->parent_));
// If we have a sibling, then our parent just went from being a 1,2
// unary node into a 1,1 binary node and no action needs to be taken.
RawPtrType parent = node_ns->parent_;
auto parent_ns = &NodeTraits::node_state(*parent);
if (internal::valid_sentinel_ptr(parent_ns->left_) &&
internal::valid_sentinel_ptr(parent_ns->right_))
return;
// We have no sibling, therefor we just changed our parent from a 1,1
// leaf node into a 0,1 unary node. The WAVL rank rule states that all
// rank differences are 1 or 2; 0 is not allowed. Rebalance the tree in
// order to restore the rule.
//
// Start with the promotions. Pseudo code is...
//
// while (IsValid(parent) && parent is a 0,1 node)
// promote parent
// climb up the tree
bool node_parity;
bool parent_parity;
bool sibling_parity;
bool is_left_child;
do {
// Promote
parent_ns->promote_rank();
Observer::RecordInsertPromote();
// Climb
node = parent;
node_ns = &NodeTraits::node_state(*node);
parent = node_ns->parent_;
// If we have run out of room to climb, then we must be done.
if (!internal::valid_sentinel_ptr(parent))
return;
// We have a parent. Determine the relationship between our rank
// parity, our parent's rank parity, and our sibling's rank parity
// (if any). Note; null children have a rank of -1, therefor the
// rank parity of a sibling is always odd (1). In the process, make
// a note of whether we are our parent's left or right child.
parent_ns = &NodeTraits::node_state(*parent);
is_left_child = (parent_ns->left_ == node);
if (is_left_child) {
sibling_parity = internal::valid_sentinel_ptr(parent_ns->right_)
? NodeTraits::node_state(*parent_ns->right_).rank_parity()
: true;
} else {
ZX_DEBUG_ASSERT(parent_ns->right_ == node);
sibling_parity = internal::valid_sentinel_ptr(parent_ns->left_)
? NodeTraits::node_state(*parent_ns->left_).rank_parity()
: true;
}
node_parity = node_ns->rank_parity();
parent_parity = parent_ns->rank_parity();
// We need to keep promoting and climbing if we just turned our new
// parent into a 0,1 node. Let N, P and S denote the current node,
// parent, and sibling parities. Working out the truth tables, our
// parent is now a 0,1 node iff (!N * !P * S) + (N * P * !S)
} while ((!node_parity && !parent_parity && sibling_parity) ||
(node_parity && parent_parity && !sibling_parity));
// OK. At this point, we know that we have a parent, and our parent is
// not a 0,1 node. Either our rank rule has been restored and we are
// done, or our parent is 0,2 and we need to perform either one or two
// rotations. Start by checking to see if our parent is 0,2. Using the
// notation from above...
//
// ++ our parent is a 0,2 node iff (!N * !P * !S) + (N * P * S).
// ++ which implies that we are finished iff (N != P) + (N != S)
//
if ((node_parity != parent_parity) || (node_parity != sibling_parity))
return;
// Looks like our parent is a 0,2 node. Perform the post-insert fixup
// operations, forward if we are our parent's left child, reverse if we
// are our parent's right child.
if (is_left_child)
PostInsertFixupLR<ForwardTraits>(node, parent);
else
PostInsertFixupLR<ReverseTraits>(node, parent);
}
// BalancePostErase_Fix22Leaf
//
// Called after an erase operation which erased the 1-child of a node.
// Checks to see if the node has become a 2,2 leaf node and takes
// appropriate action to restore the rank rule if needed.
void BalancePostErase_Fix22Leaf(RawPtrType node) {
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node));
// If we just turned node into a 2,2 leaf, it will have no children and
// odd rank-parity. If it has even parity, or any children at all,
// there is nothing we need to do.
auto& ns = NodeTraits::node_state(*node);
if (!ns.rank_parity() || internal::valid_sentinel_ptr(ns.left_) ||
internal::valid_sentinel_ptr(ns.right_))
return;
// Demote the node turning it into a 1,1 leaf.
ns.demote_rank();
Observer::RecordEraseDemote();
// By demoting this node, we may have just created a 3-child. Find its
// parent, figure out if it was the left or right child, then use the
// FixLR3Child method to check for the 3-child case and deal with it if
// we need to. If this node had no parent, then we know that we are
// finished.
ZX_DEBUG_ASSERT(ns.parent_ != nullptr);
if (internal::is_sentinel_ptr(ns.parent_))
return;
auto& parent_ns = NodeTraits::node_state(*ns.parent_);
bool is_left_child = parent_ns.left_ == node;
ZX_DEBUG_ASSERT(is_left_child || (parent_ns.right_ == node));
if (is_left_child)
BalancePostErase_FixLR3Child<ForwardTraits>(ns.parent_);
else
BalancePostErase_FixLR3Child<ReverseTraits>(ns.parent_);
}
// BalancePostErase_FixLR3Child<LRTraits>
//
// Called during post-erase rebalancing when it is possible that a 3-child
// may have been created. There are 3 ways that this may have happened.
//
// 1) A node's 2-child leaf node was erased making the link to null a
// 3-child link.
// 2) A node's 2-child unary node was erased making the promoted node
// a 3-child.
// 3) During rebalancing fix-up of a 2,2 leaf, the 2,2 leaf node is a
// 2-child. Demoting it to turn it into a 1,1 leaf makes it a 3-child.
//
// Note: this method is templated using LRTraits because the method needs to
// know which link may have become a 3-child based on prior circumstances.
// Without this knowledge, it is not possible to detect a 3 child using only
// rank parity. For this method, the LR-Child of 'node' is known to now be
// either a 2-child or a 3-child, it cannot be a 1-child.
template <typename LRTraits>
void BalancePostErase_FixLR3Child(RawPtrType node) {
using RLTraits = typename LRTraits::Inverse;
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(node));
// Throughout this method, we will use the following notation.
//
// Let...
// Z = The node with the (potential) 3-child.
// X = The (potential) 3-child.
// Y = X's sibling. (if any)
//
RawPtrType Z = node;
auto Z_ns = &NodeTraits::node_state(*Z);
RawPtrType X = LRTraits::LRChild(*Z_ns);
// Check to see if X is a 3-child of Z. We know it is if...
// 1) X exists and Z has odd rank parity
// 2) X does not exist and Z has even rank parity
if (internal::valid_sentinel_ptr(X) != Z_ns->rank_parity())
return;
// Phase 1, demotions.
//
// While X is a 3-child and Y is a 2-child, or a 2,2 node
// Demote Y if it is is a 2,2 node
// Demote Z
// Climb (set Z = Z-parent, update definitions of X and Y)
//
bool X_is_LR_child = true;
RawPtrType Y = LRTraits::RLChild(*Z_ns);
while (true) {
// We know that X is a 3 child, Determine the status of Y. We know
// that whenever X is a 3-child that Y must exist because...
//
// 1) Z's rank is at least 2. In the case that X does not exist, X's rank
// is -1 so Z's rank is (-1 + 3) = 2. If X does exist, Z's rank
// is even larger.
// 2) The rank rule for the Y,Z relationship should currently hold,
// meaning that the rank difference between Y and Z is either 1 or 2,
// therefor Y's rank is at least 0.
// 3) Because Y has non-negative rank, it must exist.
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(Y));
auto& Y_ns = NodeTraits::node_state(*Y);
bool Y_is_2_child = (Y_ns.rank_parity() == Z_ns->rank_parity());
// Our next steps are already determined and don't involve Y if
// Y is currently a 2 child. Don't bother to compute
// Y_is_22_node if we already know that Y is a 2-child.
if (!Y_is_2_child) {
bool Y_is_22_node;
if (Y_ns.rank_parity()) {
// If Y has odd rank parity, it is a 2,2 node if both its
// children have odd parity, meaning each child either does
// not exist, or exists and has odd parity..
Y_is_22_node = ((!internal::valid_sentinel_ptr(Y_ns.left_) ||
NodeTraits::node_state(*Y_ns.left_).rank_parity()) &&
(!internal::valid_sentinel_ptr(Y_ns.right_) ||
NodeTraits::node_state(*Y_ns.right_).rank_parity()));
} else {
// If Y has even rank parity, it can only be a 2,2 node if it is
// a binary node and both of its children have even parity.
Y_is_22_node = internal::valid_sentinel_ptr(Y_ns.left_) &&
internal::valid_sentinel_ptr(Y_ns.right_) &&
!NodeTraits::node_state(*Y_ns.left_).rank_parity() &&
!NodeTraits::node_state(*Y_ns.right_).rank_parity();
}
// If Y is neither a 2-child or a 2,2 node, then we are done
// with phase 1. X is still a 3-child, but it will take one or
// more rotations to fix the problem.
if (!Y_is_22_node)
break;
}
// Demote Z. If Y was a 1-child, demote Y as well.
Z_ns->demote_rank();
Observer::RecordEraseDemote();
if (!Y_is_2_child) {
Y_ns.demote_rank();
Observer::RecordEraseDemote();
}
// Climb. If we cannot climb because we have no parent, or we can
// climb and the new X is no longer a 3-child, then we are done
// with the rebalance operation.
if (!internal::valid_sentinel_ptr(Z_ns->parent_))
return;
bool X_rank_parity = Z_ns->rank_parity();
X = Z;
Z = Z_ns->parent_;
Z_ns = &NodeTraits::node_state(*Z);
if (Z_ns->rank_parity() == X_rank_parity)
return;
X_is_LR_child = (LRTraits::LRChild(*Z_ns) == X);
Y = X_is_LR_child ? LRTraits::RLChild(*Z_ns) : LRTraits::LRChild(*Z_ns);
}
// Phase 2, rotations
//
// At this point, we know...
//
// 1) Z exists
// 2) X is a 3-child of Z (but may not exist).
// 3) Y exists (because X is a 3-child of Z, see above)
// 4) Y is not a 2-child of Z (so, Z is a 1,3 node)
// 5) Y is not a 2,2 node.
//
// We will need to perform either 1 or 2 rotations to fix this problem.
// Which directions we rotate will now depend on whether or not X is a
// left or right child of Z. Invoke the post-erase rotation method
// using the traits which will normalize the notation so that X is an
// LR-child of Z.
if (X_is_LR_child)
BalancePostErase_DoRotations<LRTraits>(Y, Z);
else
BalancePostErase_DoRotations<RLTraits>(Y, Z);
}
// BalancePostErase_DoRotations<LRTraits>
//
// Refer to the notes at the end of BalancePostErase_FixLR3Child<LRTraits>
// for a description of the current situation. In addition to the
// assertions there, we also now know that X is the LR-Child of Z (and
// therefor Y is the RL-child of Z) (note; X is only indirectly involved
// here, so not passed as an argument).
template <typename LRTraits>
void BalancePostErase_DoRotations(RawPtrType Y, RawPtrType Z) {
using RLTraits = typename LRTraits::Inverse;
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(Y));
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(Z));
auto& Y_ns = NodeTraits::node_state(*Y);
auto& Z_ns = NodeTraits::node_state(*Z);
// Let...
// V = the LR-child of Y
// W = the RL-child of Y
//
// Perform rotations to fix the fact that X is a 3-child of Z. We will
// need to do 1 of 2 things depending on whether W is a 1-child or
// 2-child of Y.
//
RawPtrType W = LRTraits::RLChild(Y_ns);
bool W_rank_parity =
internal::valid_sentinel_ptr(W) ? NodeTraits::node_state(*W).rank_parity() : true;
if (Y_ns.rank_parity() != W_rank_parity) {
// Case #1: W is a 1-child of Y
//
// 1) Rotate-LR at Y
// 2) Promote Y
// 3a) If Z is a leaf, demote it twice.
// 3b) else demote Z once.
RotateLR<LRTraits>(Y, Z);
Observer::RecordEraseRotation();
Y_ns.promote_rank();
if (!internal::valid_sentinel_ptr(Z_ns.left_) && !internal::valid_sentinel_ptr(Z_ns.right_)) {
Z_ns.double_demote_rank();
} else {
Z_ns.demote_rank();
}
} else {
// Case #2: W is a 2-child of Y
//
// 1) Rotate-RL at V
// 2) Rotate-LR at V
// 3) Promote V twice.
// 3) Demote Y once.
// 3) Demote Z twice.
RawPtrType V = LRTraits::LRChild(Y_ns);
ZX_DEBUG_ASSERT(internal::valid_sentinel_ptr(V)); // V must exist
auto& V_ns = NodeTraits::node_state(*V);
ZX_DEBUG_ASSERT(V_ns.rank_parity() != Y_ns.rank_parity()); // V must be a 1-child of Y
// TODO(johngro) : Special case the implementation of a double
// rotation operation. It would almost certainly be more efficient
// than performing 2 sequential single rotation operations.
RotateLR<RLTraits>(V, Y);
RotateLR<LRTraits>(V, Z);
Observer::RecordEraseDoubleRotation();
V_ns.double_promote_rank();
Y_ns.demote_rank();
Z_ns.double_demote_rank();
}
}
// Tree state consists of...
//
// 1) a root node
// 2) a left-most node
// 3) a right-most node
// 4) a count of nodes
//
// Technically, only #1 is required. #2-4 are optimizations to assist in
// iteration and size operations.
RawPtrType root_ = nullptr;
RawPtrType left_most_ = sentinel();
RawPtrType right_most_ = sentinel();
size_t count_ = 0;
};
// TaggedWAVLTree<> is intended for use with ContainableBaseClasses<>.
//
// For an easy way to allow instances of your class to live in multiple
// intrusive containers at once, simply derive from
// ContainableBaseClasses<YourContainables<PtrType, TagType>...> and then use
// this template instead of WAVLTree<> as the container, passing the same tag
// type you used earlier as the third parameter.
//
// See comments on ContainableBaseClasses<> in fbl/intrusive_container_utils.h
// for more details.
//
template <typename KeyType, typename PtrType, typename TagType,
typename KeyTraits = DefaultKeyedObjectTraits<
KeyType, typename internal::ContainerPtrTraits<PtrType>::ValueType>,
typename Observer = tests::intrusive_containers::DefaultWAVLTreeObserver>
using TaggedWAVLTree = WAVLTree<KeyType, PtrType, KeyTraits, TagType,
DefaultWAVLTreeTraits<PtrType, TagType>, Observer>;
template <typename PtrType, typename TagType, NodeOptions Options = NodeOptions::None>
using TaggedWAVLTreeContainable = WAVLTreeContainable<PtrType, Options, TagType>;
} // namespace fbl
#endif // FBL_INTRUSIVE_WAVL_TREE_H_