| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_containers/internal/aa_tree_item.h" |
| |
| namespace pw::containers::internal { |
| |
| uint8_t AATreeItem::GetLevel() const { |
| return parent_.packed_value() | (left_.packed_value() << 2) | |
| (right_.packed_value() << 4); |
| } |
| |
| void AATreeItem::SetLevel(uint8_t level) { |
| parent_.set_packed_value(level & 3); |
| left_.set_packed_value((level >> 2) & 3); |
| right_.set_packed_value((level >> 4) & 3); |
| } |
| |
| bool AATreeItem::IsMapped() const { |
| return GetLevel() != 0 || parent_.get() != nullptr || |
| left_.get() != nullptr || right_.get() != nullptr; |
| } |
| |
| size_t AATreeItem::GetTreeSize() { |
| return 1 + (left_.get() == nullptr ? 0 : left_->GetTreeSize()) + |
| (right_.get() == nullptr ? 0 : right_->GetTreeSize()); |
| } |
| |
| AATreeItem* AATreeItem::GetRoot() { |
| AATreeItem* node = this; |
| while (node->parent_.get() != nullptr) { |
| node = node->parent_.get(); |
| } |
| return node; |
| } |
| |
| AATreeItem* AATreeItem::GetLeftmost() { |
| return left_.get() == nullptr ? this : left_->GetLeftmost(); |
| } |
| |
| AATreeItem* AATreeItem::GetRightmost() { |
| return right_.get() == nullptr ? this : right_->GetRightmost(); |
| } |
| |
| AATreeItem* AATreeItem::GetPredecessor() { |
| if (left_.get() != nullptr) { |
| return left_->GetRightmost(); |
| } |
| const AATreeItem* current = this; |
| AATreeItem* ancestor = parent_.get(); |
| while (ancestor != nullptr && ancestor->left_.get() == current) { |
| current = ancestor; |
| ancestor = ancestor->parent_.get(); |
| } |
| return ancestor; |
| } |
| |
| AATreeItem* AATreeItem::GetSuccessor() { |
| if (right_.get() != nullptr) { |
| return right_->GetLeftmost(); |
| } |
| const AATreeItem* current = this; |
| AATreeItem* ancestor = parent_.get(); |
| while (ancestor != nullptr && ancestor->right_.get() == current) { |
| current = ancestor; |
| ancestor = ancestor->parent_.get(); |
| } |
| return ancestor; |
| } |
| |
| AATreeItem* AATreeItem::Unmap() { |
| AATreeItem* node; |
| if (left_.get() == nullptr && right_.get() == nullptr) { |
| // Leaf node. |
| node = nullptr; |
| |
| } else if (left_.get() == nullptr) { |
| // Replace the node with the next one in value. |
| node = GetSuccessor(); |
| right_->parent_.set(nullptr); |
| node->SetRight(node->Unmap()); |
| |
| } else { |
| // Replace the node with the previous one in value. |
| node = GetPredecessor(); |
| left_->parent_.set(nullptr); |
| node->SetLeft(node->Unmap()); |
| node->SetRight(right_.get()); |
| } |
| if (parent_.get() != nullptr) { |
| parent_->Replace(this, node); |
| } else if (node == nullptr) { |
| // Removing the only node from the tree. |
| Reset(); |
| return nullptr; |
| } |
| node = node == nullptr ? GetRoot() : node->Rebalance(); |
| Reset(); |
| return node; |
| } |
| |
| void AATreeItem::SetLeft(AATreeItem* left) { |
| if (left != nullptr) { |
| left->parent_.set(this); |
| } |
| left_.set(left); |
| } |
| |
| void AATreeItem::SetRight(AATreeItem* right) { |
| if (right != nullptr) { |
| right->parent_.set(this); |
| } |
| right_.set(right); |
| } |
| |
| void AATreeItem::Replace(AATreeItem* old_child, AATreeItem* new_child) { |
| if (left_.get() == old_child) { |
| SetLeft(new_child); |
| } else if (right_.get() == old_child) { |
| SetRight(new_child); |
| } |
| } |
| |
| AATreeItem* AATreeItem::Skew() { |
| if (left_.get() == nullptr || GetLevel() != left_->GetLevel()) { |
| return this; |
| } |
| AATreeItem* skewed = left_.get(); |
| SetLeft(skewed->right_.get()); |
| skewed->parent_.set(parent_.get()); |
| skewed->SetRight(this); |
| return skewed; |
| } |
| |
| AATreeItem* AATreeItem::Split() { |
| if (right_.get() == nullptr || right_->right_.get() == nullptr || |
| GetLevel() != right_->right_->GetLevel()) { |
| return this; |
| } |
| AATreeItem* split = right_.get(); |
| SetRight(split->left_.get()); |
| split->parent_.set(parent_.get()); |
| split->SetLeft(this); |
| split->SetLevel(split->GetLevel() + 1); |
| return split; |
| } |
| |
| AATreeItem* AATreeItem::Rebalance() { |
| AATreeItem* node = this; |
| while (true) { |
| uint8_t left_level = |
| node->left_.get() == nullptr ? 0 : node->left_->GetLevel(); |
| uint8_t right_level = |
| node->right_.get() == nullptr ? 0 : node->right_->GetLevel(); |
| uint8_t new_level = std::min(left_level, right_level) + 1; |
| if (new_level < node->GetLevel()) { |
| node->SetLevel(new_level); |
| if (new_level < right_level) { |
| node->right_->SetLevel(new_level); |
| } |
| } |
| AATreeItem* parent = node->parent_.get(); |
| AATreeItem* orig = node; |
| node = node->Skew(); |
| if (node->right_.get() != nullptr) { |
| node->SetRight(node->right_->Skew()); |
| if (node->right_->right_.get() != nullptr) { |
| node->right_->SetRight(node->right_->right_->Skew()); |
| } |
| } |
| node = node->Split(); |
| if (node->right_.get() != nullptr) { |
| node->SetRight(node->right_->Split()); |
| } |
| if (parent == nullptr) { |
| return node; |
| } |
| parent->Replace(orig, node); |
| node = parent; |
| } |
| } |
| |
| void AATreeItem::Clear() { |
| if (parent_.get() != nullptr) { |
| parent_->Replace(this, nullptr); |
| } |
| if (left_.get() != nullptr) { |
| left_->Clear(); |
| } |
| if (right_.get() != nullptr) { |
| right_->Clear(); |
| } |
| Reset(); |
| } |
| |
| void AATreeItem::Reset() { |
| parent_ = PackedPtr<AATreeItem>(); |
| left_ = PackedPtr<AATreeItem>(); |
| right_ = PackedPtr<AATreeItem>(); |
| } |
| |
| } // namespace pw::containers::internal |