| // 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_INTERNAL_H_ |
| #define FBL_INTRUSIVE_WAVL_TREE_INTERNAL_H_ |
| |
| #include <stdint.h> |
| |
| #include <fbl/intrusive_container_utils.h> |
| |
| namespace fbl { |
| |
| namespace tests { |
| namespace intrusive_containers { |
| // Fwd decl of sanity checker class used by tests. |
| class WAVLTreeChecker; |
| |
| // Definition of the default (no-op) Observer. |
| // |
| // Observers are used by the test framework to record the number of insert, |
| // erase, rank-promote, rank-demote and rotation operations performed during |
| // usage. The DefaultWAVLTreeObserver does nothing and should fall out of the |
| // code during template expansion. |
| // |
| // Observers may also be used to maintain additional application-specific per- |
| // node invariants. For example, maintaining subtree min/max values is useful |
| // for multikey partition searching. |
| // |
| // Note: Records of promotions and demotions are used by tests to demonstrate |
| // that the computational complexity of insert/erase rebalancing is amortized |
| // constant. Promotions and demotions which are side effects of the rotation |
| // phase of rebalancing are considered to be part of the cost of rotation and |
| // are not tallied in the overall promote/demote accounting. |
| // |
| struct DefaultWAVLTreeObserver { |
| // Invoked on the newly inserted node before rebalancing. |
| template <typename Iter> |
| static void RecordInsert(Iter node) {} |
| |
| // Invoked on the node to be inserted and each ancestor node while traversing |
| // the tree to find the initial insertion point. |
| template <typename T, typename Iter> |
| static void RecordInsertTraverse(T* node, Iter ancestor) {} |
| |
| // Invoked on the node to be inserted and the colliding node with the same |
| // key, during an insert_or_find operation. This method is mutually exclusive |
| // with RecordInsertReplace, only one or the other is invoked during an |
| // insert operation. |
| template <typename T, typename Iter> |
| static void RecordInsertCollision(T* node, Iter collision) {} |
| |
| // Invoked on an existing node and its replacement, before swapping the |
| // replacement into the tree, during an insert_or_replace operation. This |
| // method is mutually exclusive with RecordInsertCollision, only one or the |
| // other is invoked during an insert operation. |
| template <typename Iter, typename T> |
| static void RecordInsertReplace(Iter node, T* replacement) {} |
| |
| // Invoked after each promotion during post-insert rebalancing. |
| static void RecordInsertPromote() {} |
| |
| // Invoked after a single rotation during post-insert rebalancing. |
| static void RecordInsertRotation() {} |
| |
| // Invoked after a double rotation during post-insert rebalancing. |
| static void RecordInsertDoubleRotation() {} |
| |
| // Invoked on the pivot node, its parent, children, and sibling before a |
| // rotation, just before updating the pointers in the relevant nodes. The |
| // chirality of the children and sibling is relative to the direction of |
| // rotation. The direction of rotation can be determined by comparing these |
| // arguments with the values returned by the left() and right() iterator |
| // methods of the pivot or parent arguments. |
| // |
| // The following diagrams the relationship of the nodes in a left rotation: |
| // |
| // pivot parent | |
| // / \ / \ | |
| // parent rl_child <----------- sibling pivot | |
| // / \ / \ | |
| // sibling lr_child lr_child rl_child | |
| // |
| // In a right rotation, all of the relationships are reflected. However, the |
| // left() and right() iterator methods of each node return unreflected values. |
| // |
| template <typename Iter> |
| static void RecordRotation(Iter pivot, Iter lr_child, Iter rl_child, Iter parent, Iter sibling) {} |
| |
| // Invoked on the node to be erased and the node in the tree where the |
| // augmented invariants become invalid, leading up to the root. Called just |
| // after updating the pointers in the relevant nodes, but before rebalancing. |
| // |
| // The following diagrams the relationship of the erased and invalidated |
| // nodes: |
| // |
| // root | |
| // / \ | |
| // A B <---- Invalidated starting here on up to the root | |
| // / \ / \ | |
| // C D E F <---- Erased node | |
| // |
| // When the node to be erased has two children, it is first swapped with the |
| // leftmost child of the righthand subtree. In this case the invalidated node |
| // is the parent of the original leftmost child of the righthand subtree, as |
| // this is the deepest node to change after erasure. |
| // |
| // root root | |
| // / \ / \ | |
| // A B A B | |
| // / \ / \ / \ / \ | |
| // C D E F <--+ C D E H <---- Invalidated starting here | |
| // / \ | Swap / \ | |
| // G H <-+ G F <---- Erased node | |
| // |
| template <typename T, typename Iter> |
| static void RecordErase(T* node, Iter invalidated) {} |
| |
| // Invoked after each demotion during post-erase rebalancing. |
| static void RecordEraseDemote() {} |
| |
| // Invoked after each single rotation during post-erase rebalancing. |
| static void RecordEraseRotation() {} |
| |
| // Invoked after each dobule rotation during post-erase rebalancing. |
| static void RecordEraseDoubleRotation() {} |
| |
| template <typename TreeType> |
| static void VerifyRankRule(const TreeType& tree, typename TreeType::RawPtrType node) {} |
| |
| template <typename TreeType> |
| static void VerifyBalance(const TreeType& tree, uint64_t depth) {} |
| }; |
| |
| } // namespace intrusive_containers |
| } // namespace tests |
| |
| using DefaultWAVLTreeRankType = bool; |
| |
| // Prototypes for the WAVL tree node state. By default, we just use a bool to |
| // record the rank parity of a node. During testing, however, we actually use a |
| // specialized version of the node state in which the rank is stored as an |
| // int32_t so that extra sanity checks can be made during balance testing. |
| // |
| // Note: All of the data members are stored in the node state base, as are the |
| // helper methods IsValid and InContainer. Only the rank manipulation methods |
| // are in the derived WAVLTreeNodeState class. This is to ensure that that the |
| // WAVLTreeNodeState<> struct is a "standard layout type" which allows objects |
| // which include a WAVLTreeNodeState<> to be standard layout types, provided |
| // that they follow all of the other the rules as well. |
| // |
| template <typename PtrType, NodeOptions Options, typename RankType> // Fwd decl |
| struct WAVLTreeNodeStateBase; |
| template <typename PtrType, NodeOptions Options = NodeOptions::None, |
| typename RankType = DefaultWAVLTreeRankType> // Partial spec |
| struct WAVLTreeNodeState; |
| |
| template <typename PtrType, NodeOptions Options> |
| struct WAVLTreeNodeState<PtrType, Options, int32_t> |
| : public WAVLTreeNodeStateBase<PtrType, Options, int32_t> { |
| bool rank_parity() const { return ((this->rank_ & 0x1) != 0); } |
| void promote_rank() { this->rank_ += 1; } |
| void double_promote_rank() { this->rank_ += 2; } |
| void demote_rank() { this->rank_ -= 1; } |
| void double_demote_rank() { this->rank_ -= 2; } |
| }; |
| |
| } // namespace fbl |
| |
| #endif // FBL_INTRUSIVE_WAVL_TREE_INTERNAL_H_ |