[libc-commits] [libc] [libc][tsearch] add tsearch functions (PR #172625)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Wed Dec 17 18:17:42 PST 2025
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/172625
>From 1588de4b6f0da428155f4175f23043c292fb6575 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Mon, 15 Dec 2025 03:09:11 -0500
Subject: [PATCH 01/20] [libc][tsearch] scaffold WAVL header
---
libc/src/__support/CMakeLists.txt | 9 +++++++++
libc/src/__support/weak_avl.h | 19 +++++++++++++++++++
2 files changed, 28 insertions(+)
create mode 100644 libc/src/__support/weak_avl.h
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index c7f127d6934a0..99a1641d629f3 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -391,6 +391,15 @@ add_header_library(
libc.src.__support.macros.attributes
)
+add_header_library(
+ weak_avl
+ HDRS
+ weak_avl.h
+ DEPENDS
+ libc.src.__support.CPP.new
+ libc.src.__support.macros.config
+)
+
add_subdirectory(FPUtil)
add_subdirectory(OSUtil)
add_subdirectory(StringUtil)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
new file mode 100644
index 0000000000000..817146e8b9ab9
--- /dev/null
+++ b/libc/src/__support/weak_avl.h
@@ -0,0 +1,19 @@
+//===-- Implementation header for weak AVL tree -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
+#define LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
+
+#include "src/__support/CPP/new.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
>From c58c5373397e2c3353e934edbe8a51d0ee5c80be Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Mon, 15 Dec 2025 16:42:22 -0500
Subject: [PATCH 02/20] stage
---
libc/src/__support/weak_avl.h | 97 +++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 817146e8b9ab9..552f6bf4e47be 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -5,15 +5,112 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+// Weak AVL tree implementation based on the algorithm described in:
+// 1. https://maskray.me/blog/2025-12-14-weak-avl-tree
+// 2. https://reviews.freebsd.org/D25480
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
#define LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
+#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/utility/move.h"
+#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
+namespace wavl {
+struct WeakAVLDefaultCompare {
+ template <typename T>
+ LIBC_INLINE bool operator()(const T &a, const T &b) const {
+ return a < b;
+ }
+};
+
+template <typename T, typename Compare = WeakAVLDefaultCompare>
+class WeakAVLNode {
+ // Data
+ T data;
+
+ // Packs the parent pointer with 2 flag bits in the low bits. Bit 0 indicates
+ // whether the left child has rank difference 2; bit 1 indicates whether the
+ // right child has rank difference 2. A cleared bit means rank difference 1.
+ uintptr_t parent_and_flags;
+
+ // Children pointers
+ WeakAVLNode *children[2];
+
+ // Constants
+ static LIBC_INLINE_VAR constexpr uintptr_t FLAGS_MASK = 0b11;
+ static LIBC_INLINE_VAR constexpr uintptr_t LEFT_FLAG_BIT = 0b01;
+ static LIBC_INLINE_VAR constexpr uintptr_t RIGHT_FLAG_BIT = 0b10;
+ static_assert(alignof(WeakAVLNode) >= 4,
+ "WeakAVLNode alignment must be at least 4 to store flags.");
+
+ // Auxiliary methods for accessing fields
+ LIBC_INLINE WeakAVLNode *parent() const {
+ return cpp::bit_cast<WeakAVLNode *>(parent_and_flags & ~FLAGS_MASK);
+ }
+ LIBC_INLINE uintptr_t flags() const { return parent_and_flags & FLAGS_MASK; }
+ LIBC_INLINE void set_parent(WeakAVLNode *p) {
+ parent_and_flags = cpp::bit_cast<uintptr_t>(p) | flags();
+ }
+ LIBC_INLINE bool has_rank_diff_2(bool is_right) const {
+ return flags() & (is_right ? RIGHT_FLAG_BIT : LEFT_FLAG_BIT);
+ }
+ LIBC_INLINE void toggle_rank_diff_2(bool is_right) {
+ parent_and_flags ^= (is_right ? RIGHT_FLAG_BIT : LEFT_FLAG_BIT);
+ }
+ LIBC_INLINE void clear_flags() { parent_and_flags &= ~FLAGS_MASK; }
+ LIBC_INLINE bool operator<(const WeakAVLNode &other) const {
+ return Compare{}(data, other.data);
+ }
+
+ LIBC_INLINE WeakAVLNode(T data)
+ : data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
+ }
+
+public:
+ LIBC_INLINE static WeakAVLNode *create(T value) {
+ AllocChecker ac;
+ WeakAVLNode *res = ::new (ac) WeakAVLNode(value);
+ if (ac)
+ return res;
+ return nullptr;
+ }
+ LIBC_INLINE static void destroy(WeakAVLNode *node) {
+ if (!node)
+ return;
+ destroy(node->children[0]);
+ destroy(node->children[1]);
+ ::delete node;
+ }
+ LIBC_INLINE static WeakAVLNode *rotate(WeakAVLNode *&root, WeakAVLNode *node,
+ bool is_right) {
+ WeakAVLNode *pivot = node->children[is_right];
+ // Handover pivot's child
+ WeakAVLNode *grandchild = pivot->children[!is_right];
+ node->children[is_right] = grandchild;
+ if (grandchild)
+ grandchild->set_parent(node);
+ pivot->set_parent(node->parent());
+ // Pivot becomes the new root of the subtree
+ if (!node->parent())
+ root = pivot;
+ else {
+ bool node_is_right = node->parent()->children[1] == node;
+ node->parent()->children[node_is_right] = pivot;
+ }
+ pivot->children[!is_right] = node;
+ node->set_parent(pivot);
+ return pivot;
+ }
+};
+} // namespace wavl
+
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
>From 1b6a1d6f689144c41d915dbd8ec874f4adc9300c Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Mon, 15 Dec 2025 17:01:06 -0500
Subject: [PATCH 03/20] stage
---
libc/src/__support/weak_avl.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 552f6bf4e47be..965dc69e4c530 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -108,6 +108,9 @@ class WeakAVLNode {
node->set_parent(pivot);
return pivot;
}
+ LIBC_INLINE static void insert(WeakAVLNode*& root, WeakAVLNode* node) {
+
+ }
};
} // namespace wavl
>From e3b68949d5dfbf18b3097cbe78421f16b73e23eb Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Mon, 15 Dec 2025 22:52:14 -0500
Subject: [PATCH 04/20] stage the work
---
libc/src/__support/CMakeLists.txt | 5 +
libc/src/__support/weak_avl.h | 172 +++++++++++++++++++---
libc/test/src/__support/CMakeLists.txt | 10 ++
libc/test/src/__support/weak_avl_test.cpp | 128 ++++++++++++++++
4 files changed, 296 insertions(+), 19 deletions(-)
create mode 100644 libc/test/src/__support/weak_avl_test.cpp
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 99a1641d629f3..bde12abaa291e 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -396,7 +396,12 @@ add_header_library(
HDRS
weak_avl.h
DEPENDS
+ libc.hdr.stdint_proxy
+ libc.src.__support.CPP.bit
libc.src.__support.CPP.new
+ libc.src.__support.CPP.utility
+ libc.src.__support.libc_assert
+ libc.src.__support.macros.attributes
libc.src.__support.macros.config
)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 965dc69e4c530..aa38199cc1bfa 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -17,27 +17,20 @@
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/utility/move.h"
+#include "src/__support/libc_assert.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
-namespace wavl {
-struct WeakAVLDefaultCompare {
- template <typename T>
- LIBC_INLINE bool operator()(const T &a, const T &b) const {
- return a < b;
- }
-};
-
-template <typename T, typename Compare = WeakAVLDefaultCompare>
-class WeakAVLNode {
+template <typename T> class WeakAVLNode {
// Data
T data;
// Packs the parent pointer with 2 flag bits in the low bits. Bit 0 indicates
// whether the left child has rank difference 2; bit 1 indicates whether the
// right child has rank difference 2. A cleared bit means rank difference 1.
+ // All rank differences are 1 or 2, and every leaf has rank 0.
uintptr_t parent_and_flags;
// Children pointers
@@ -47,8 +40,6 @@ class WeakAVLNode {
static LIBC_INLINE_VAR constexpr uintptr_t FLAGS_MASK = 0b11;
static LIBC_INLINE_VAR constexpr uintptr_t LEFT_FLAG_BIT = 0b01;
static LIBC_INLINE_VAR constexpr uintptr_t RIGHT_FLAG_BIT = 0b10;
- static_assert(alignof(WeakAVLNode) >= 4,
- "WeakAVLNode alignment must be at least 4 to store flags.");
// Auxiliary methods for accessing fields
LIBC_INLINE WeakAVLNode *parent() const {
@@ -65,15 +56,13 @@ class WeakAVLNode {
parent_and_flags ^= (is_right ? RIGHT_FLAG_BIT : LEFT_FLAG_BIT);
}
LIBC_INLINE void clear_flags() { parent_and_flags &= ~FLAGS_MASK; }
- LIBC_INLINE bool operator<(const WeakAVLNode &other) const {
- return Compare{}(data, other.data);
+ LIBC_INLINE void set_flags(uintptr_t flags) {
+ parent_and_flags |= (FLAGS_MASK & flags);
}
-
LIBC_INLINE WeakAVLNode(T data)
: data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
}
-public:
LIBC_INLINE static WeakAVLNode *create(T value) {
AllocChecker ac;
WeakAVLNode *res = ::new (ac) WeakAVLNode(value);
@@ -81,6 +70,16 @@ class WeakAVLNode {
return res;
return nullptr;
}
+
+public:
+ LIBC_INLINE const WeakAVLNode *get_left() const { return children[0]; }
+ LIBC_INLINE const WeakAVLNode *get_right() const { return children[1]; }
+ LIBC_INLINE const T &get_data() const { return data; }
+ LIBC_INLINE bool is_rank_diff_2(bool is_right) const {
+ return has_rank_diff_2(is_right);
+ }
+
+ // Destroy the subtree rooted at node
LIBC_INLINE static void destroy(WeakAVLNode *node) {
if (!node)
return;
@@ -88,6 +87,16 @@ class WeakAVLNode {
destroy(node->children[1]);
::delete node;
}
+ // Rotate the subtree rooted at node in the given direction.
+ //
+ // Illustration for is_right = true (Left Rotation):
+ //
+ // (Node) (Pivot)
+ // / \ / \
+ // A (Pivot) => (Node) C
+ // / \ / \
+ // B C A B
+ //
LIBC_INLINE static WeakAVLNode *rotate(WeakAVLNode *&root, WeakAVLNode *node,
bool is_right) {
WeakAVLNode *pivot = node->children[is_right];
@@ -108,11 +117,136 @@ class WeakAVLNode {
node->set_parent(pivot);
return pivot;
}
- LIBC_INLINE static void insert(WeakAVLNode*& root, WeakAVLNode* node) {
-
+ // Insert data into the subtree rooted at root.
+ // Returns the node if insertion is successful or the node exists in
+ // the tree.
+ // Returns nullptr if memory allocation fails.
+ // `Compare` returns integer values for ternary comparison.
+ template <typename Compare>
+ LIBC_INLINE static WeakAVLNode *find_or_insert(WeakAVLNode *&root, T data,
+ Compare &&comp) {
+ WeakAVLNode *parent = nullptr, *cursor = root;
+ bool is_right = false;
+ while (cursor != nullptr) {
+ parent = cursor;
+ int comp_result = comp(parent->data, data);
+ if (comp_result == 0)
+ return parent; // Node already exists
+ is_right = comp_result < 0;
+ cursor = cursor->children[is_right];
+ }
+ WeakAVLNode *allocated = create(cpp::move(data));
+ if (!allocated)
+ return nullptr;
+ WeakAVLNode *node = allocated;
+ node->set_parent(parent);
+
+ // Case 0: inserting into an empty tree
+ if (!parent) {
+ root = node; // Tree was empty
+ return node;
+ }
+
+ parent->children[is_right] = node;
+ // Rebalance process
+ while (parent) {
+ // Case 1: parent does not need to be promoted as node is lowering
+ // than the parent by 2 ranks.
+ // (P) (P)
+ // / \ / \
+ // 2 1 => 1 1
+ // / \ / \
+ // (N) (*) (N) (*)
+ if (parent->has_rank_diff_2(is_right)) {
+ parent->toggle_rank_diff_2(is_right);
+ break;
+ }
+
+ bool sibling_has_rank_diff_2 = parent->has_rank_diff_2(!is_right);
+ // Case 2: node's sibling has rank-difference 1.
+ // Promoting parent will fix the conflict of the trinodes but we may need
+ // to continue on parent.
+ //
+ // (GP) (GP)
+ // | Promote | x - 1
+ // | x -----> (P)
+ // 0 | / 1 / \
+ // (N) --- (P) ---- (N) \ 2
+ // \ 1 \
+ // (S) (S)
+ if (!sibling_has_rank_diff_2) {
+ parent->toggle_rank_diff_2(!is_right);
+ node = parent;
+ parent = node->parent();
+ continue;
+ }
+
+ LIBC_ASSERT((node->flags() != 0b11) &&
+ "there should be no 2-2 nodes in a weak AVL tree");
+
+ LIBC_ASSERT((node == allocated || node->flags() != 0) &&
+ "Internal node must have a child with rank-difference 2, "
+ "otherwise it should have already been handled.");
+
+ bool node_is_right = (node == parent->children[1]);
+ // Case 3: node's sibling has rank-difference 2. And node has a 1-node
+ // along the same direction. We can do a single rotation to fix the
+ // trinode.
+ // (GP) (GP)
+ // 0 | X Rotate |
+ // (N) ----- (P) => (N)
+ // 1 / \ 2 \ 2 1 / \ 1
+ // (C1) \ \ (C1) (P)
+ // (C2) (S) 1 / \ 1
+ // (C2) (S)
+ if (node->has_rank_diff_2(!node_is_right)) {
+ WeakAVLNode *new_subroot = rotate(root, parent, node_is_right);
+ new_subroot->clear_flags();
+ parent->clear_flags();
+ break;
+ }
+ // Case 4: node's sibling has rank-difference 2. And node has a 1-node
+ // along the opposite direction. We need a double rotation to fix the
+ // trinode.
+ // (GP) (GP)
+ // 0 | X Zig-Zag | X
+ // (N) ----- (P) => (C1)
+ // 2 / \ 1 \ 2 1 / \ 1
+ // / (C1) \ (N) (P)
+ // (C2) L / \ R (S) 1 / \ L R / \ 1
+ // (A) (B) (C2) (A)(B) (S)
+ // (mirrored)
+ // (GP) (GP)
+ // X | 0 Zig-Zag | X
+ // (P) ----- (N) => (C1)
+ // 2 / 1 / \ 2 1 / \ 1
+ // / (C1) \ (P) (N)
+ // (S) L / \ R (C2) 1 / \ L R / \ 1
+ // (A) (B) (S)(A) (B)(C2)
+ WeakAVLNode *subroot1 =
+ rotate(root, node, !node_is_right); // First rotation
+ [[maybe_unused]] WeakAVLNode *subroot2 =
+ rotate(root, parent, node_is_right); // Second rotation
+ LIBC_ASSERT(subroot1 == subroot2 &&
+ "Subroots after double rotation should be the same");
+ uintptr_t flags = subroot1->flags();
+ node->clear_flags();
+ parent->clear_flags();
+ subroot1->clear_flags();
+ // Select destinations
+ WeakAVLNode *dst_left = node_is_right ? parent : node;
+ WeakAVLNode *dst_right = node_is_right ? node : parent;
+ // Masked toggles
+ if (flags & LEFT_FLAG_BIT)
+ dst_left->toggle_rank_diff_2(true);
+
+ if (flags & RIGHT_FLAG_BIT)
+ dst_right->toggle_rank_diff_2(false);
+ break;
+ }
+ return allocated;
}
};
-} // namespace wavl
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt
index 138866b4cc869..97ee42347c9f3 100644
--- a/libc/test/src/__support/CMakeLists.txt
+++ b/libc/test/src/__support/CMakeLists.txt
@@ -280,6 +280,16 @@ add_libc_test(
libc.src.__support.CPP.bit
)
+add_libc_test(
+ weak_avl_test
+ SUITE
+ libc-support-tests
+ SRCS
+ weak_avl_test.cpp
+ DEPENDS
+ libc.src.__support.weak_avl
+)
+
add_subdirectory(CPP)
add_subdirectory(File)
add_subdirectory(RPC)
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
new file mode 100644
index 0000000000000..daddc047ab9f0
--- /dev/null
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -0,0 +1,128 @@
+//===-- Unittests for WeakAVL ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/weak_avl.h"
+#include "test/UnitTest/Test.h"
+
+using Node = LIBC_NAMESPACE::WeakAVLNode<int>;
+
+namespace {
+
+// Validate weak-AVL rank-difference invariant:
+// no node may have rank-difference 2 on both sides.
+bool validate(const Node *node) {
+ if (!node)
+ return true;
+ bool left_2 = node->is_rank_diff_2(false);
+ bool right_2 = node->is_rank_diff_2(true);
+ return (!left_2 || !right_2) && validate(node->get_left()) &&
+ validate(node->get_right());
+}
+
+// Insert according to pattern `next(i)`
+using NextFn = int (*)(int);
+
+static Node *build_tree(NextFn next, int N, int (*compare)(int, int)) {
+ Node *root = nullptr;
+ for (int i = 0; i < N; ++i)
+ Node::find_or_insert(root, next(i), compare);
+ return root;
+}
+
+// Insertion patterns
+
+static int seq(int i) { return i; }
+
+static int rev(int i) {
+ constexpr int N = 1000;
+ return N - 1 - i;
+}
+
+// Coprime stride permutation: i -> (i * X) % N
+static int stride(int i) {
+ constexpr int N = 1000;
+ constexpr int X = 7919; // gcd(X, N) = 1
+ return (i * X) % N;
+}
+
+} // namespace
+
+TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
+ Node *root = nullptr;
+ auto compare = [](int a, int b) { return a - b; };
+
+ Node *node10 = Node::find_or_insert(root, 10, compare);
+ ASSERT_TRUE(node10 != nullptr);
+ ASSERT_EQ(root, node10);
+ ASSERT_TRUE(validate(root));
+
+ Node *node5 = Node::find_or_insert(root, 5, compare);
+ ASSERT_TRUE(node5 != nullptr);
+ ASSERT_TRUE(validate(root));
+
+ Node *node15 = Node::find_or_insert(root, 15, compare);
+ ASSERT_TRUE(node15 != nullptr);
+ ASSERT_TRUE(validate(root));
+
+ Node *node10_again = Node::find_or_insert(root, 10, compare);
+ ASSERT_EQ(node10, node10_again);
+ ASSERT_TRUE(validate(root));
+
+ Node::destroy(root);
+}
+
+TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(seq, N, compare);
+ ASSERT_TRUE(validate(root));
+
+ for (int i = 0; i < N; ++i) {
+ Node *node = Node::find_or_insert(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+ ASSERT_EQ(node->get_data(), i);
+ }
+
+ ASSERT_TRUE(validate(root));
+ Node::destroy(root);
+}
+
+TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(rev, N, compare);
+ ASSERT_TRUE(validate(root));
+
+ for (int i = 0; i < N; ++i) {
+ Node *node = Node::find_or_insert(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+ ASSERT_EQ(node->get_data(), i);
+ }
+
+ ASSERT_TRUE(validate(root));
+ Node::destroy(root);
+}
+
+TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(stride, N, compare);
+ ASSERT_TRUE(validate(root));
+
+ for (int i = 0; i < N; ++i) {
+ Node *node = Node::find_or_insert(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+ ASSERT_EQ(node->get_data(), i);
+ }
+
+ ASSERT_TRUE(validate(root));
+ Node::destroy(root);
+}
>From 5640d0fb67d07611a2ef98eb3413c794e9b6a35a Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Mon, 15 Dec 2025 23:04:49 -0500
Subject: [PATCH 05/20] stage the work
---
libc/src/__support/weak_avl.h | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index aa38199cc1bfa..492e8df0e32d9 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -150,6 +150,7 @@ template <typename T> class WeakAVLNode {
parent->children[is_right] = node;
// Rebalance process
while (parent) {
+ is_right = (parent->children[1] == node);
// Case 1: parent does not need to be promoted as node is lowering
// than the parent by 2 ranks.
// (P) (P)
@@ -188,7 +189,6 @@ template <typename T> class WeakAVLNode {
"Internal node must have a child with rank-difference 2, "
"otherwise it should have already been handled.");
- bool node_is_right = (node == parent->children[1]);
// Case 3: node's sibling has rank-difference 2. And node has a 1-node
// along the same direction. We can do a single rotation to fix the
// trinode.
@@ -199,8 +199,8 @@ template <typename T> class WeakAVLNode {
// (C1) \ \ (C1) (P)
// (C2) (S) 1 / \ 1
// (C2) (S)
- if (node->has_rank_diff_2(!node_is_right)) {
- WeakAVLNode *new_subroot = rotate(root, parent, node_is_right);
+ if (node->has_rank_diff_2(!is_right)) {
+ WeakAVLNode *new_subroot = rotate(root, parent, is_right);
new_subroot->clear_flags();
parent->clear_flags();
break;
@@ -223,10 +223,9 @@ template <typename T> class WeakAVLNode {
// / (C1) \ (P) (N)
// (S) L / \ R (C2) 1 / \ L R / \ 1
// (A) (B) (S)(A) (B)(C2)
- WeakAVLNode *subroot1 =
- rotate(root, node, !node_is_right); // First rotation
+ WeakAVLNode *subroot1 = rotate(root, node, !is_right); // First rotation
[[maybe_unused]] WeakAVLNode *subroot2 =
- rotate(root, parent, node_is_right); // Second rotation
+ rotate(root, parent, is_right); // Second rotation
LIBC_ASSERT(subroot1 == subroot2 &&
"Subroots after double rotation should be the same");
uintptr_t flags = subroot1->flags();
@@ -234,8 +233,8 @@ template <typename T> class WeakAVLNode {
parent->clear_flags();
subroot1->clear_flags();
// Select destinations
- WeakAVLNode *dst_left = node_is_right ? parent : node;
- WeakAVLNode *dst_right = node_is_right ? node : parent;
+ WeakAVLNode *dst_left = is_right ? parent : node;
+ WeakAVLNode *dst_right = is_right ? node : parent;
// Masked toggles
if (flags & LEFT_FLAG_BIT)
dst_left->toggle_rank_diff_2(true);
>From 4b58c2bf2a2a5b008c262f4093d7ccf497374550 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 01:01:33 -0500
Subject: [PATCH 06/20] update ref
---
libc/src/__support/weak_avl.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 492e8df0e32d9..7ce862d6d78b3 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -8,6 +8,7 @@
// Weak AVL tree implementation based on the algorithm described in:
// 1. https://maskray.me/blog/2025-12-14-weak-avl-tree
// 2. https://reviews.freebsd.org/D25480
+// 3. https://ics.uci.edu/~goodrich/teach/cs165/notes/WeakAVLTrees.pdf
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
>From 287cfe8d88e3ac35fe378856cd0fefc4e82f0a33 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 05:04:29 -0500
Subject: [PATCH 07/20] update
---
libc/src/__support/weak_avl.h | 225 +++++++++++++++++++++++++++++++++-
1 file changed, 224 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 7ce862d6d78b3..0b46f9179ab12 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -21,6 +21,7 @@
#include "src/__support/libc_assert.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
+#include <cstdint>
namespace LIBC_NAMESPACE_DECL {
@@ -60,6 +61,9 @@ template <typename T> class WeakAVLNode {
LIBC_INLINE void set_flags(uintptr_t flags) {
parent_and_flags |= (FLAGS_MASK & flags);
}
+ LIBC_INLINE void is_external() {
+ return (children[0] == nullptr) && (children[1] == nullptr);
+ }
LIBC_INLINE WeakAVLNode(T data)
: data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
}
@@ -72,6 +76,76 @@ template <typename T> class WeakAVLNode {
return nullptr;
}
+ // Unlink a node from tree. The corresponding flag is not update. The node is
+ // not deleted and its pointers are not cleared.
+ // FixupSite is the lowest surviving node from which rank/flag invariants may
+ // be violated.
+ struct FixupSite {
+ WeakAVLNode *parent;
+ bool is_right;
+ };
+ LIBC_INLINE static FixupSite unlink(WeakAVLNode *&root, WeakAVLNode *node) {
+ bool has_left = node->children[0] != nullptr;
+ bool has_right = node->children[1] != nullptr;
+
+ // Case 0. node has no children
+ if (!has_left && !has_right) {
+ if (node->parent() == nullptr) {
+ root = nullptr; // Tree becomes empty
+ return {nullptr, false};
+ }
+ FixupSite site = {node->parent(), node->parent()->children[1] == node};
+ site.parent->children[site.is_right] = nullptr;
+ return site;
+ }
+
+ // Case 1. node has only one child
+ if (has_left != has_right) {
+ if (!node->parent()) {
+ root = node->children[has_right];
+ root->set_parent(nullptr);
+ return {nullptr, false};
+ }
+ FixupSite site = {node->parent(), node->parent()->children[1] == node};
+ site.parent->children[site.is_right] = node->children[has_right];
+ node->children[has_right]->set_parent(site.parent);
+ return site;
+ }
+
+ // Case 2. node has two children
+ // We cannot swap field as tree node must be valid outside.
+ WeakAVLNode *replacement = node->children[1];
+ while (replacement->children[0] != nullptr)
+ replacement = replacement->children[0];
+ bool replacement_was_right =
+ (replacement->parent()->children[1] == replacement);
+ WeakAVLNode *replacement_parent = replacement->parent();
+ WeakAVLNode *replacement_rchild = replacement->children[1];
+ replacement->set_parent(node->parent());
+ replacement->set_flags(node->flags());
+ replacement->children[0] = node->children[0];
+ replacement->children[1] = node->children[1] == replacement
+ ? replacement_rchild
+ : node->children[1];
+ if (replacement->children[0])
+ replacement->children[0]->set_parent(replacement);
+ if (replacement->children[1])
+ replacement->children[1]->set_parent(replacement);
+ if (replacement->parent()) {
+ bool node_was_right = node->parent()->children[1] == node;
+ replacement->parent()->children[node_was_right] = replacement;
+ } else
+ root = replacement;
+ FixupSite site = {replacement_parent == node ? replacement
+ : replacement_parent,
+ replacement_was_right};
+ if (replacement_rchild)
+ replacement_rchild->set_parent(site.parent);
+ if (site.parent)
+ site.parent->children[site.is_right] = replacement_rchild;
+ return site;
+ }
+
public:
LIBC_INLINE const WeakAVLNode *get_left() const { return children[0]; }
LIBC_INLINE const WeakAVLNode *get_right() const { return children[1]; }
@@ -118,6 +192,22 @@ template <typename T> class WeakAVLNode {
node->set_parent(pivot);
return pivot;
}
+
+ // Find data in the subtree rooted at root. If not found, returns nullptr.
+ // `Compare` returns integer values for ternary comparison.
+ template <typename Compare>
+ LIBC_INLINE static WeakAVLNode *find(WeakAVLNode *root, T data,
+ Compare &&comp) {
+ WeakAVLNode *cursor = root;
+ while (cursor != nullptr) {
+ int comp_result = comp(cursor->data, data);
+ if (comp_result == 0)
+ return cursor; // Node found
+ bool is_right = comp_result < 0;
+ cursor = cursor->children[is_right];
+ }
+ return nullptr; // Node not found
+ }
// Insert data into the subtree rooted at root.
// Returns the node if insertion is successful or the node exists in
// the tree.
@@ -184,7 +274,7 @@ template <typename T> class WeakAVLNode {
}
LIBC_ASSERT((node->flags() != 0b11) &&
- "there should be no 2-2 nodes in a weak AVL tree");
+ "there should be no 2-2 node along the insertion fixup path");
LIBC_ASSERT((node == allocated || node->flags() != 0) &&
"Internal node must have a child with rank-difference 2, "
@@ -246,6 +336,139 @@ template <typename T> class WeakAVLNode {
}
return allocated;
}
+
+ // Erase the node from the tree rooted at root.
+ LIBC_INLINE static void erase(WeakAVLNode *&root, WeakAVLNode *node) {
+ // Unlink the node from the tree
+ auto [cursor, is_right] = unlink(root, node);
+ ::delete node;
+ while (cursor) {
+ // Case 0. cursor previously had rank-difference 1 on the side of the
+ // deleted node. We can simply update the rank-difference and stop.
+ // Notice that this step may create 2-2 nodes, thus deviate from "strong"
+ // AVL tree.
+ //
+ // (C) (C)
+ // X / \ 1 => X / \
+ // (*) (D) (*) \ 2
+ // (D)
+ if (!cursor->has_rank_diff_2(is_right)) {
+ cursor->toggle_rank_diff_2(is_right);
+ break;
+ }
+
+ // Case 1. cursor previously had rank-difference 2 on the side of the
+ // deleted node. Now it has rank-difference 3, which violates the
+ // weak-AVL property. We found that we have a sibling with rank-difference
+ // 2, so we can demote cursor and continue upwards.
+ //
+ // (P) (P)
+ // | X | (X + 1)
+ // (C) |
+ // / \ => (C)
+ // 2 / \ 1 / \
+ // (*) \ 3 (*) \ 2
+ // (D) (D)
+ if (cursor->has_rank_diff_2(!is_right)) {
+ cursor->toggle_rank_diff_2(!is_right);
+ if (cursor->parent())
+ is_right = (cursor->parent()->children[1] == cursor);
+ cursor = cursor->parent();
+ continue;
+ }
+
+ // Case 2. continue from Case 1; but the sibling has rank-difference 1.
+ // However, we found that the sibling is a 2-2 node. We demote both
+ // sibling and cursor, and continue upwards.
+ //
+ // (P) (P)
+ // | X | (X + 1)
+ // (C) |
+ // 1 / \ => (C)
+ // (S) \ 1 / \
+ // / \ \ 3 (S) \ 2
+ // 2 / \ 2 (D) 1 / \ 1 (D)
+ // (*) (*) (*) (*)
+ WeakAVLNode *sibling = cursor->children[!is_right];
+ LIBC_ASSERT(sibling && "rank-difference 1 sibling cannot be empty");
+ if (sibling->flags() == 0b11) {
+ sibling->clear_flags();
+ if (cursor->parent())
+ is_right = (cursor->parent()->children[1] == cursor);
+ cursor = cursor->parent();
+ continue;
+ }
+
+ // Case 3. continue from Case 2; but the sibling cannot be demoted.
+ // Sibling has a node T along the same direction with rank-difference 1.
+ //
+ // (P) (P)
+ // | X | X
+ // (C) (S)
+ // 1 / \ Rotate 2 / \ 1
+ // (S) \ => / (C)
+ // 1 / \ Y \ 3 (T) Y / \ 2
+ // (T) \ (D) (*) \
+ // (*) (D)
+ bool sibling_is_right = !is_right;
+ if (!sibling->has_rank_diff_2(sibling_is_right)) {
+ WeakAVLNode *new_subroot = rotate(root, cursor, sibling_is_right);
+ LIBC_ASSERT(new_subroot == sibling &&
+ "sibling should become the subtree root");
+ // Update flags
+ bool sibling_alter_child_has_rank_diff_2 =
+ new_subroot->has_rank_diff_2(!sibling_is_right);
+ new_subroot->clear_flags();
+ new_subroot->toggle_rank_diff_2(sibling_is_right);
+
+ // Cursor only needs to be updated if it become a 2-2 node
+ if (sibling_alter_child_has_rank_diff_2) {
+ // Demote a 2-2 cursor if it is also external
+ if (cursor->is_external()) {
+ cursor->clear_flags();
+ new_subroot->toggle_rank_diff_2(!sibling_is_right);
+ LIBC_ASSERT(new_subroot->flags() == 0b00 &&
+ "sibling should become a 1-1 node.");
+ } else {
+ cursor->toggle_rank_diff_2(sibling_is_right);
+ LIBC_ASSERT(cursor->flags() == 0b11 &&
+ "cursor should become a 2-2 node.");
+ }
+ }
+ break;
+ }
+ // Case 4. continue from Case 3; but rank-difference 1 child T of sibling
+ // is on the opposite direction.
+ //
+ // (P) (P)
+ // | X | X
+ // (C) Zig-Zag (T)
+ // 1 / \ => / \
+ // (S) \ 2 / \ 2
+ // / \ 1 \ 3 (S) (C)
+ // 2 / (T) (D) 1 / Y \ / Z \ 1
+ // (*) Y / \ Z (*) (A)(B) (D)
+ // (A) (B)
+ WeakAVLNode *target_child = rotate(root, sibling, !sibling_is_right);
+ uintptr_t flags = target_child->flags();
+ WeakAVLNode *new_subroot = rotate(root, cursor, sibling_is_right);
+ LIBC_ASSERT(new_subroot == target_child &&
+ "target_child should become the subtree root");
+ // Set flags
+ target_child->set_flags(0b11);
+ cursor->clear_flags();
+ sibling->clear_flags();
+ // Select destinations
+ WeakAVLNode *dst_left = sibling_is_right ? cursor : sibling;
+ WeakAVLNode *dst_right = sibling_is_right ? sibling : cursor;
+ // Masked toggles
+ if (flags & LEFT_FLAG_BIT)
+ dst_left->toggle_rank_diff_2(true);
+ if (flags & RIGHT_FLAG_BIT)
+ dst_right->toggle_rank_diff_2(false);
+ break;
+ }
+ }
};
} // namespace LIBC_NAMESPACE_DECL
>From 912a18f3efaca15641e4bddce17c878ea20cde77 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 05:31:20 -0500
Subject: [PATCH 08/20] update
---
libc/src/__support/weak_avl.h | 11 +-
libc/test/src/__support/weak_avl_test.cpp | 152 +++++++++++++++++++---
2 files changed, 137 insertions(+), 26 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 0b46f9179ab12..1e6b6c4d25fe4 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -21,7 +21,6 @@
#include "src/__support/libc_assert.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
-#include <cstdint>
namespace LIBC_NAMESPACE_DECL {
@@ -59,10 +58,10 @@ template <typename T> class WeakAVLNode {
}
LIBC_INLINE void clear_flags() { parent_and_flags &= ~FLAGS_MASK; }
LIBC_INLINE void set_flags(uintptr_t flags) {
- parent_and_flags |= (FLAGS_MASK & flags);
+ parent_and_flags = (parent_and_flags & ~FLAGS_MASK) | (flags & FLAGS_MASK);
}
- LIBC_INLINE void is_external() {
- return (children[0] == nullptr) && (children[1] == nullptr);
+ LIBC_INLINE bool is_external() {
+ return (children[0] == nullptr) || (children[1] == nullptr);
}
LIBC_INLINE WeakAVLNode(T data)
: data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
@@ -427,8 +426,8 @@ template <typename T> class WeakAVLNode {
if (cursor->is_external()) {
cursor->clear_flags();
new_subroot->toggle_rank_diff_2(!sibling_is_right);
- LIBC_ASSERT(new_subroot->flags() == 0b00 &&
- "sibling should become a 1-1 node.");
+ LIBC_ASSERT(new_subroot->flags() == 0b11 &&
+ "sibling should become a 2-2 node.");
} else {
cursor->toggle_rank_diff_2(sibling_is_right);
LIBC_ASSERT(cursor->flags() == 0b11 &&
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index daddc047ab9f0..18384ed07265a 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -13,15 +13,19 @@ using Node = LIBC_NAMESPACE::WeakAVLNode<int>;
namespace {
-// Validate weak-AVL rank-difference invariant:
-// no node may have rank-difference 2 on both sides.
-bool validate(const Node *node) {
+// Validate weak-AVL rank-difference invariant assuming **pure insertion only**
+// (i.e. no erasure has occurred).
+//
+// NOTE: This validator is intentionally *not* correct after erase(), because
+// weak-AVL allows transient or permanent 2-2 configurations during deletion
+// fixup.
+bool validate_pure_insertion(const Node *node) {
if (!node)
return true;
bool left_2 = node->is_rank_diff_2(false);
bool right_2 = node->is_rank_diff_2(true);
- return (!left_2 || !right_2) && validate(node->get_left()) &&
- validate(node->get_right());
+ return (!left_2 || !right_2) && validate_pure_insertion(node->get_left()) &&
+ validate_pure_insertion(node->get_right());
}
// Insert according to pattern `next(i)`
@@ -35,7 +39,6 @@ static Node *build_tree(NextFn next, int N, int (*compare)(int, int)) {
}
// Insertion patterns
-
static int seq(int i) { return i; }
static int rev(int i) {
@@ -44,12 +47,19 @@ static int rev(int i) {
}
// Coprime stride permutation: i -> (i * X) % N
-static int stride(int i) {
+static int stride(int i, int prime = 7919) {
constexpr int N = 1000;
- constexpr int X = 7919; // gcd(X, N) = 1
- return (i * X) % N;
+ return (i * prime) % N;
}
+// Thin wrappers to make test intent explicit.
+template <typename Compare>
+static Node *find(Node *root, int value, Compare &&comp) {
+ return Node::find(root, value, comp);
+}
+
+static void erase(Node *&root, Node *node) { Node::erase(root, node); }
+
} // namespace
TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
@@ -59,19 +69,19 @@ TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
Node *node10 = Node::find_or_insert(root, 10, compare);
ASSERT_TRUE(node10 != nullptr);
ASSERT_EQ(root, node10);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node *node5 = Node::find_or_insert(root, 5, compare);
ASSERT_TRUE(node5 != nullptr);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node *node15 = Node::find_or_insert(root, 15, compare);
ASSERT_TRUE(node15 != nullptr);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node *node10_again = Node::find_or_insert(root, 10, compare);
ASSERT_EQ(node10, node10_again);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node::destroy(root);
}
@@ -81,7 +91,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
constexpr int N = 1000;
Node *root = build_tree(seq, N, compare);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
for (int i = 0; i < N; ++i) {
Node *node = Node::find_or_insert(root, i, compare);
@@ -89,7 +99,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
ASSERT_EQ(node->get_data(), i);
}
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node::destroy(root);
}
@@ -98,7 +108,7 @@ TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
constexpr int N = 1000;
Node *root = build_tree(rev, N, compare);
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
for (int i = 0; i < N; ++i) {
Node *node = Node::find_or_insert(root, i, compare);
@@ -106,7 +116,7 @@ TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
ASSERT_EQ(node->get_data(), i);
}
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
Node::destroy(root);
}
@@ -114,8 +124,8 @@ TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
auto compare = [](int a, int b) { return a - b; };
constexpr int N = 1000;
- Node *root = build_tree(stride, N, compare);
- ASSERT_TRUE(validate(root));
+ Node *root = build_tree([](int i) { return stride(i); }, N, compare);
+ ASSERT_TRUE(validate_pure_insertion(root));
for (int i = 0; i < N; ++i) {
Node *node = Node::find_or_insert(root, i, compare);
@@ -123,6 +133,108 @@ TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
ASSERT_EQ(node->get_data(), i);
}
- ASSERT_TRUE(validate(root));
+ ASSERT_TRUE(validate_pure_insertion(root));
+ Node::destroy(root);
+}
+
+TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(seq, N, compare);
+ ASSERT_TRUE(validate_pure_insertion(root));
+
+ for (int i = 0; i < N; ++i) {
+ Node *node = find(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+ ASSERT_EQ(node->get_data(), i);
+ }
+
+ ASSERT_TRUE(find(root, -1, compare) == nullptr);
+ ASSERT_TRUE(find(root, N, compare) == nullptr);
+ ASSERT_TRUE(find(root, 2 * N, compare) == nullptr);
+
+ Node::destroy(root);
+}
+
+TEST(LlvmLibcWeakAVLTest, SequentialErase) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(seq, N, compare);
+
+ for (int i = 0; i < N; ++i) {
+ Node *node = find(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+
+ erase(root, node);
+ ASSERT_TRUE(find(root, i, compare) == nullptr);
+ }
+
+ ASSERT_TRUE(root == nullptr);
+}
+
+TEST(LlvmLibcWeakAVLTest, ReverseErase) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(seq, N, compare);
+
+ for (int i = N - 1; i >= 0; --i) {
+ Node *node = find(root, i, compare);
+ ASSERT_TRUE(node != nullptr);
+
+ erase(root, node);
+ ASSERT_TRUE(find(root, i, compare) == nullptr);
+ }
+
+ ASSERT_TRUE(root == nullptr);
+}
+
+TEST(LlvmLibcWeakAVLTest, StridedErase) {
+ auto compare = [](int a, int b) { return a - b; };
+ constexpr int N = 1000;
+
+ Node *root = build_tree(seq, N, compare);
+
+ for (int i = 0; i < N; ++i) {
+ int key = stride(i, 5261);
+ Node *node = find(root, key, compare);
+ ASSERT_TRUE(node != nullptr);
+
+ erase(root, node);
+ ASSERT_TRUE(find(root, key, compare) == nullptr);
+ }
+
+ ASSERT_TRUE(root == nullptr);
+}
+
+TEST(LlvmLibcWeakAVLTest, EraseStructuralCases) {
+ auto compare = [](int a, int b) { return a - b; };
+
+ Node *root = nullptr;
+ int keys[] = {10, 5, 15, 3, 7, 12, 18};
+
+ for (int k : keys)
+ Node::find_or_insert(root, k, compare);
+
+ // Erase leaf.
+ erase(root, find(root, 3, compare));
+ ASSERT_TRUE(find(root, 3, compare) == nullptr);
+
+ // Erase internal nodes.
+ erase(root, find(root, 5, compare));
+ ASSERT_TRUE(find(root, 5, compare) == nullptr);
+
+ erase(root, find(root, 10, compare));
+ ASSERT_TRUE(find(root, 10, compare) == nullptr);
+
+ int attempts[] = {7, 12, 15, 18};
+ for (int k : attempts) {
+ Node *n = find(root, k, compare);
+ ASSERT_TRUE(n != nullptr);
+ ASSERT_EQ(n->get_data(), k);
+ }
+
Node::destroy(root);
}
>From 0e6d925313e9db6642cfa18218d4f601e026c811 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 05:47:34 -0500
Subject: [PATCH 09/20] update
---
libc/src/__support/weak_avl.h | 90 ++++++++++++++++++++---------------
1 file changed, 51 insertions(+), 39 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 1e6b6c4d25fe4..b8c4c9811f4c9 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -87,10 +87,10 @@ template <typename T> class WeakAVLNode {
bool has_left = node->children[0] != nullptr;
bool has_right = node->children[1] != nullptr;
- // Case 0. node has no children
+ // Case 0: no children
if (!has_left && !has_right) {
- if (node->parent() == nullptr) {
- root = nullptr; // Tree becomes empty
+ if (!node->parent()) {
+ root = nullptr;
return {nullptr, false};
}
FixupSite site = {node->parent(), node->parent()->children[1] == node};
@@ -98,50 +98,62 @@ template <typename T> class WeakAVLNode {
return site;
}
- // Case 1. node has only one child
+ // Case 1: one child
if (has_left != has_right) {
+ WeakAVLNode *child = node->children[has_right];
if (!node->parent()) {
- root = node->children[has_right];
- root->set_parent(nullptr);
+ root = child;
+ child->set_parent(nullptr);
return {nullptr, false};
}
FixupSite site = {node->parent(), node->parent()->children[1] == node};
- site.parent->children[site.is_right] = node->children[has_right];
- node->children[has_right]->set_parent(site.parent);
+ site.parent->children[site.is_right] = child;
+ child->set_parent(site.parent);
return site;
}
- // Case 2. node has two children
- // We cannot swap field as tree node must be valid outside.
- WeakAVLNode *replacement = node->children[1];
- while (replacement->children[0] != nullptr)
- replacement = replacement->children[0];
- bool replacement_was_right =
- (replacement->parent()->children[1] == replacement);
- WeakAVLNode *replacement_parent = replacement->parent();
- WeakAVLNode *replacement_rchild = replacement->children[1];
- replacement->set_parent(node->parent());
- replacement->set_flags(node->flags());
- replacement->children[0] = node->children[0];
- replacement->children[1] = node->children[1] == replacement
- ? replacement_rchild
- : node->children[1];
- if (replacement->children[0])
- replacement->children[0]->set_parent(replacement);
- if (replacement->children[1])
- replacement->children[1]->set_parent(replacement);
- if (replacement->parent()) {
- bool node_was_right = node->parent()->children[1] == node;
- replacement->parent()->children[node_was_right] = replacement;
- } else
- root = replacement;
- FixupSite site = {replacement_parent == node ? replacement
- : replacement_parent,
- replacement_was_right};
- if (replacement_rchild)
- replacement_rchild->set_parent(site.parent);
- if (site.parent)
- site.parent->children[site.is_right] = replacement_rchild;
+ // Case 2: two children: replace by successor (leftmost in right subtree)
+ WeakAVLNode *succ = node->children[1];
+ while (succ->children[0])
+ succ = succ->children[0];
+
+ WeakAVLNode *succ_parent = succ->parent();
+ bool succ_was_right =
+ succ_parent->children[1] == succ; // true only if succ_parent==node
+ WeakAVLNode *succ_rchild = succ->children[1];
+
+ // 1) Splice successor out of its old position (flags intentionally
+ // unchanged)
+ FixupSite site = {succ_parent, succ_was_right};
+ succ_parent->children[succ_was_right] = succ_rchild;
+ if (succ_rchild)
+ succ_rchild->set_parent(succ_parent);
+
+ // 2) Transplant successor into node's position
+ succ->set_parent(node->parent());
+ succ->set_flags(
+ node->flags()); // key-swap emulation: successor takes node's rank/flags
+
+ succ->children[0] = node->children[0];
+ succ->children[1] = node->children[1];
+ if (succ->children[0])
+ succ->children[0]->set_parent(succ);
+ if (succ->children[1])
+ succ->children[1]->set_parent(succ);
+
+ if (succ->parent()) {
+ bool node_was_right = succ->parent()->children[1] == node;
+ succ->parent()->children[node_was_right] = succ;
+ } else {
+ root = succ;
+ }
+
+ // 3) If the physical removal was under `node`, fixup parent must be the
+ // successor (since `node` is deleted and successor now occupies that
+ // spot).
+ if (site.parent == node)
+ site.parent = succ;
+
return site;
}
>From a4b2e83664a8f6856f62050dd1e22cdc6c15fded Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 06:42:51 -0500
Subject: [PATCH 10/20] update
---
libc/src/__support/weak_avl.h | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index b8c4c9811f4c9..f3a3adf67ce75 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -60,8 +60,8 @@ template <typename T> class WeakAVLNode {
LIBC_INLINE void set_flags(uintptr_t flags) {
parent_and_flags = (parent_and_flags & ~FLAGS_MASK) | (flags & FLAGS_MASK);
}
- LIBC_INLINE bool is_external() {
- return (children[0] == nullptr) || (children[1] == nullptr);
+ LIBC_INLINE bool is_leaf() {
+ return (children[0] == nullptr) && (children[1] == nullptr);
}
LIBC_INLINE WeakAVLNode(T data)
: data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
@@ -365,6 +365,14 @@ template <typename T> class WeakAVLNode {
// (D)
if (!cursor->has_rank_diff_2(is_right)) {
cursor->toggle_rank_diff_2(is_right);
+ // If we created a 2-2 leaf, we must demote it and continue.
+ if (cursor->flags() == 0b11 && cursor->is_leaf()) {
+ cursor->clear_flags();
+ if (cursor->parent())
+ is_right = (cursor->parent()->children[1] == cursor);
+ cursor = cursor->parent();
+ continue;
+ }
break;
}
@@ -434,8 +442,8 @@ template <typename T> class WeakAVLNode {
// Cursor only needs to be updated if it become a 2-2 node
if (sibling_alter_child_has_rank_diff_2) {
- // Demote a 2-2 cursor if it is also external
- if (cursor->is_external()) {
+ // Demote a 2-2 cursor if it is a leaf
+ if (cursor->is_leaf()) {
cursor->clear_flags();
new_subroot->toggle_rank_diff_2(!sibling_is_right);
LIBC_ASSERT(new_subroot->flags() == 0b11 &&
>From 7ca40c642b268350ab0cf656d1300d71a7167298 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 07:17:59 -0500
Subject: [PATCH 11/20] add fuzzing
---
libc/fuzzing/__support/CMakeLists.txt | 8 ++
libc/fuzzing/__support/weak_avl_fuzz.cpp | 94 ++++++++++++++++++++++++
2 files changed, 102 insertions(+)
create mode 100644 libc/fuzzing/__support/weak_avl_fuzz.cpp
diff --git a/libc/fuzzing/__support/CMakeLists.txt b/libc/fuzzing/__support/CMakeLists.txt
index 9c674d2fb0d65..f9b49cc1b0a4b 100644
--- a/libc/fuzzing/__support/CMakeLists.txt
+++ b/libc/fuzzing/__support/CMakeLists.txt
@@ -25,6 +25,14 @@ add_libc_fuzzer(
-D__LIBC_EXPLICIT_SIMD_OPT
)
+add_libc_fuzzer(
+ weak_avl_fuzz
+ SRCS
+ weak_avl_fuzz.cpp
+ DEPENDS
+ libc.src.__support.weak_avl
+)
+
# TODO: FreeListHeap uses the _end symbol which conflicts with the _end symbol
# defined by GPU start.cpp files so for now we exclude this fuzzer on GPU.
if(LLVM_LIBC_FULL_BUILD AND NOT LIBC_TARGET_OS_IS_GPU)
diff --git a/libc/fuzzing/__support/weak_avl_fuzz.cpp b/libc/fuzzing/__support/weak_avl_fuzz.cpp
new file mode 100644
index 0000000000000..7c6e50d1fa252
--- /dev/null
+++ b/libc/fuzzing/__support/weak_avl_fuzz.cpp
@@ -0,0 +1,94 @@
+//===-- weak_avl_fuzz.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// Fuzzing test for llvm-libc weak AVL implementations.
+///
+//===----------------------------------------------------------------------===//
+#include "hdr/types/ENTRY.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// A sequence of actions:
+// - Erase: a single byte valued (5, 6 mod 7) followed by an int
+// - Find: a single byte valued (4 mod 7) followed by an int
+// - FindOrInsert: a single byte valued (0,1,2,3 mod 7) followed by an int
+extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size);
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
+ size_t max_size, unsigned int seed) {
+ size = LLVMFuzzerMutate(data, size, max_size);
+ return size / (1 + sizeof(int)) * (1 + sizeof(int));
+}
+
+class AVLTree {
+ using Node = WeakAVLNode<int>;
+ Node *root = nullptr;
+ bool reversed = false;
+ static int compare(int a, int b) { return (a > b) - (a < b); }
+ static int reverse_compare(int a, int b) { return (b > a) - (b < a); }
+
+public:
+ AVLTree(bool reversed = false) : reversed(reversed) {}
+ bool find(int key) {
+ return Node::find(root, key, reversed ? reverse_compare : compare);
+ }
+ bool find_or_insert(int key) {
+ return Node::find_or_insert(root, key,
+ reversed ? reverse_compare : compare);
+ }
+ bool erase(int key) {
+ Node *node = Node::find(root, key, reversed ? reverse_compare : compare);
+ if (node)
+ Node::erase(root, node);
+ return node;
+ }
+ ~AVLTree() { Node::destroy(root); }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ AVLTree tree1;
+ AVLTree tree2(true);
+ for (size_t i = 0; i + (1 + sizeof(int)) <= size; i += 1 + sizeof(int)) {
+ uint8_t action = data[i];
+ int key;
+ __builtin_memcpy(&key, data + i + 1, sizeof(int));
+ if (action % 7 == 4) {
+ // Find
+ bool res1 = tree1.find(key);
+ bool res2 = tree2.find(key);
+ if (res1 != res2)
+ __builtin_trap();
+
+ } else if (action % 7 == 5 || action % 7 == 6) {
+ // Erase
+ bool res1 = tree1.erase(key);
+ bool res2 = tree2.erase(key);
+ if (res1 != res2)
+ __builtin_trap();
+ if (tree1.find(key))
+ __builtin_trap();
+ if (tree2.find(key))
+ __builtin_trap();
+ } else {
+ // FindOrInsert
+ bool res1 = tree1.find_or_insert(key);
+ bool res2 = tree2.find_or_insert(key);
+ if (res1 != res2)
+ __builtin_trap();
+ if (!tree1.find(key))
+ __builtin_trap();
+ if (!tree2.find(key))
+ __builtin_trap();
+ }
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
>From 5c190538cd412776970648873e12f0fec9e7d6f4 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 07:42:06 -0500
Subject: [PATCH 12/20] reduce size for hermetic test
---
libc/test/src/__support/weak_avl_test.cpp | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index 18384ed07265a..93fc6498d1f8b 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -42,13 +42,13 @@ static Node *build_tree(NextFn next, int N, int (*compare)(int, int)) {
static int seq(int i) { return i; }
static int rev(int i) {
- constexpr int N = 1000;
+ constexpr int N = 256;
return N - 1 - i;
}
// Coprime stride permutation: i -> (i * X) % N
static int stride(int i, int prime = 7919) {
- constexpr int N = 1000;
+ constexpr int N = 256;
return (i * prime) % N;
}
@@ -88,7 +88,7 @@ TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -105,7 +105,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(rev, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -122,7 +122,7 @@ TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree([](int i) { return stride(i); }, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -139,7 +139,7 @@ TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -159,7 +159,7 @@ TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
TEST(LlvmLibcWeakAVLTest, SequentialErase) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -176,7 +176,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialErase) {
TEST(LlvmLibcWeakAVLTest, ReverseErase) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -193,7 +193,7 @@ TEST(LlvmLibcWeakAVLTest, ReverseErase) {
TEST(LlvmLibcWeakAVLTest, StridedErase) {
auto compare = [](int a, int b) { return a - b; };
- constexpr int N = 1000;
+ constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
>From 38ad8621f70a728ddf302a2b596c88a3759227a6 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 07:48:55 -0500
Subject: [PATCH 13/20] improve doc
---
libc/src/__support/weak_avl.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index f3a3adf67ce75..c0680be9bbbf6 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -79,6 +79,9 @@ template <typename T> class WeakAVLNode {
// not deleted and its pointers are not cleared.
// FixupSite is the lowest surviving node from which rank/flag invariants may
// be violated.
+ // Our tree requires value to stay in their node to maintain stable addresses.
+ // This complicates the unlink operation as the successor transplanting needs
+ // to updates all the pointers and flags.
struct FixupSite {
WeakAVLNode *parent;
bool is_right;
@@ -144,9 +147,8 @@ template <typename T> class WeakAVLNode {
if (succ->parent()) {
bool node_was_right = succ->parent()->children[1] == node;
succ->parent()->children[node_was_right] = succ;
- } else {
+ } else
root = succ;
- }
// 3) If the physical removal was under `node`, fixup parent must be the
// successor (since `node` is deleted and successor now occupies that
>From 7071a5b3189edc9218d930baf997e84d528fef46 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 16:33:40 -0500
Subject: [PATCH 14/20] add tree walk API
---
libc/src/__support/weak_avl.h | 32 ++++++++++-
libc/test/src/__support/weak_avl_test.cpp | 68 ++++++++++++++++++++---
2 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index c0680be9bbbf6..36b26de9e5271 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -210,7 +210,7 @@ template <typename T> class WeakAVLNode {
// `Compare` returns integer values for ternary comparison.
template <typename Compare>
LIBC_INLINE static WeakAVLNode *find(WeakAVLNode *root, T data,
- Compare &&comp) {
+ Compare comp) {
WeakAVLNode *cursor = root;
while (cursor != nullptr) {
int comp_result = comp(cursor->data, data);
@@ -228,7 +228,7 @@ template <typename T> class WeakAVLNode {
// `Compare` returns integer values for ternary comparison.
template <typename Compare>
LIBC_INLINE static WeakAVLNode *find_or_insert(WeakAVLNode *&root, T data,
- Compare &&comp) {
+ Compare comp) {
WeakAVLNode *parent = nullptr, *cursor = root;
bool is_right = false;
while (cursor != nullptr) {
@@ -490,6 +490,34 @@ template <typename T> class WeakAVLNode {
break;
}
}
+
+ enum struct WalkType {
+ PreOrder,
+ InOrder,
+ PostOrder,
+ Leaf,
+ };
+ template <typename Func>
+ LIBC_INLINE static void walk(WeakAVLNode *node, Func func) {
+ if (!node)
+ return;
+
+ if (node->is_leaf()) {
+ func(node, WalkType::Leaf);
+ return;
+ }
+
+ func(node, WalkType::PreOrder);
+
+ if (node->children[0])
+ walk(node->children[0], func);
+
+ func(node, WalkType::InOrder);
+
+ if (node->children[1])
+ walk(node->children[1], func);
+ func(node, WalkType::PostOrder);
+ }
};
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index 93fc6498d1f8b..962c3993904f1 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -64,7 +64,7 @@ static void erase(Node *&root, Node *node) { Node::erase(root, node); }
TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
Node *root = nullptr;
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
Node *node10 = Node::find_or_insert(root, 10, compare);
ASSERT_TRUE(node10 != nullptr);
@@ -87,7 +87,7 @@ TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
}
TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -104,7 +104,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
}
TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(rev, N, compare);
@@ -121,7 +121,7 @@ TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
}
TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree([](int i) { return stride(i); }, N, compare);
@@ -138,7 +138,7 @@ TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
}
TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -158,7 +158,7 @@ TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
}
TEST(LlvmLibcWeakAVLTest, SequentialErase) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -175,7 +175,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialErase) {
}
TEST(LlvmLibcWeakAVLTest, ReverseErase) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -192,7 +192,7 @@ TEST(LlvmLibcWeakAVLTest, ReverseErase) {
}
TEST(LlvmLibcWeakAVLTest, StridedErase) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
constexpr int N = 256;
Node *root = build_tree(seq, N, compare);
@@ -210,7 +210,7 @@ TEST(LlvmLibcWeakAVLTest, StridedErase) {
}
TEST(LlvmLibcWeakAVLTest, EraseStructuralCases) {
- auto compare = [](int a, int b) { return a - b; };
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
Node *root = nullptr;
int keys[] = {10, 5, 15, 3, 7, 12, 18};
@@ -238,3 +238,53 @@ TEST(LlvmLibcWeakAVLTest, EraseStructuralCases) {
Node::destroy(root);
}
+
+TEST(LlvmLibcTreeWalk, EraseStructuralCases) {
+ using WeakAVLNode = LIBC_NAMESPACE::WeakAVLNode<int>;
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
+
+ WeakAVLNode *root = nullptr;
+ int keys[] = {10, 5, 15, 3, 7, 12, 18};
+
+ for (int k : keys)
+ WeakAVLNode::find_or_insert(root, k, compare);
+
+ // Erase leaf.
+ erase(root, find(root, 3, compare));
+ ASSERT_TRUE(find(root, 3, compare) == nullptr);
+
+ // Erase internal nodes.
+ erase(root, find(root, 5, compare));
+ ASSERT_TRUE(find(root, 5, compare) == nullptr);
+
+ erase(root, find(root, 10, compare));
+ ASSERT_TRUE(find(root, 10, compare) == nullptr);
+
+ int attempts[] = {7, 12, 15, 18};
+ for (int k : attempts) {
+ WeakAVLNode *n = find(root, k, compare);
+ ASSERT_TRUE(n != nullptr);
+ ASSERT_EQ(n->get_data(), k);
+ }
+
+ WeakAVLNode::destroy(root);
+}
+
+TEST(LlvmLibcTreeWalk, InOrderTraversal) {
+ using WeakAVLNode = LIBC_NAMESPACE::WeakAVLNode<int>;
+ auto compare = [](int a, int b) { return (a > b) - (a < b); };
+
+ WeakAVLNode *root =
+ build_tree([](int x) { return stride(x, 1007); }, 256, compare);
+ int data[256];
+ int counter = 0;
+ WeakAVLNode::walk(root, [&](WeakAVLNode *node, WeakAVLNode::WalkType type) {
+ if (type == WeakAVLNode::WalkType::InOrder ||
+ type == WeakAVLNode::WalkType::Leaf)
+ data[counter++] = node->get_data();
+ });
+
+ for (int i = 0; i < 256; ++i)
+ ASSERT_EQ(data[i], i);
+ WeakAVLNode::destroy(root);
+}
>From 9667a0e80da26af3af64a7878d6713b6f02377b0 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 17:17:07 -0500
Subject: [PATCH 15/20] reduce test size further for hermetic tests
---
libc/test/src/__support/weak_avl_test.cpp | 26 +++++++++++------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index 962c3993904f1..6f66b11f515a3 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -12,7 +12,7 @@
using Node = LIBC_NAMESPACE::WeakAVLNode<int>;
namespace {
-
+constexpr int TEST_SIZE = 128;
// Validate weak-AVL rank-difference invariant assuming **pure insertion only**
// (i.e. no erasure has occurred).
//
@@ -42,13 +42,13 @@ static Node *build_tree(NextFn next, int N, int (*compare)(int, int)) {
static int seq(int i) { return i; }
static int rev(int i) {
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
return N - 1 - i;
}
// Coprime stride permutation: i -> (i * X) % N
static int stride(int i, int prime = 7919) {
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
return (i * prime) % N;
}
@@ -88,7 +88,7 @@ TEST(LlvmLibcWeakAVLTest, SimpleInsertion) {
TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(seq, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -105,7 +105,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialInsertion) {
TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(rev, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -122,7 +122,7 @@ TEST(LlvmLibcWeakAVLTest, ReversedInsertion) {
TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree([](int i) { return stride(i); }, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -139,7 +139,7 @@ TEST(LlvmLibcWeakAVLTest, StridedInsertion) {
TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(seq, N, compare);
ASSERT_TRUE(validate_pure_insertion(root));
@@ -159,7 +159,7 @@ TEST(LlvmLibcWeakAVLTest, FindExistingAndMissing) {
TEST(LlvmLibcWeakAVLTest, SequentialErase) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(seq, N, compare);
@@ -176,7 +176,7 @@ TEST(LlvmLibcWeakAVLTest, SequentialErase) {
TEST(LlvmLibcWeakAVLTest, ReverseErase) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(seq, N, compare);
@@ -193,7 +193,7 @@ TEST(LlvmLibcWeakAVLTest, ReverseErase) {
TEST(LlvmLibcWeakAVLTest, StridedErase) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
- constexpr int N = 256;
+ constexpr int N = TEST_SIZE;
Node *root = build_tree(seq, N, compare);
@@ -275,8 +275,8 @@ TEST(LlvmLibcTreeWalk, InOrderTraversal) {
auto compare = [](int a, int b) { return (a > b) - (a < b); };
WeakAVLNode *root =
- build_tree([](int x) { return stride(x, 1007); }, 256, compare);
- int data[256];
+ build_tree([](int x) { return stride(x, 1007); }, TEST_SIZE, compare);
+ int data[TEST_SIZE];
int counter = 0;
WeakAVLNode::walk(root, [&](WeakAVLNode *node, WeakAVLNode::WalkType type) {
if (type == WeakAVLNode::WalkType::InOrder ||
@@ -284,7 +284,7 @@ TEST(LlvmLibcTreeWalk, InOrderTraversal) {
data[counter++] = node->get_data();
});
- for (int i = 0; i < 256; ++i)
+ for (int i = 0; i < TEST_SIZE; ++i)
ASSERT_EQ(data[i], i);
WeakAVLNode::destroy(root);
}
>From d034c036d0417eabe285f74fcc7abc132df14cb9 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 17:46:28 -0500
Subject: [PATCH 16/20] add doc
---
libc/src/__support/weak_avl.h | 53 +++++++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 36b26de9e5271..2b960b7febae2 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -5,11 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-// Weak AVL tree implementation based on the algorithm described in:
-// 1. https://maskray.me/blog/2025-12-14-weak-avl-tree
-// 2. https://reviews.freebsd.org/D25480
-// 3. https://ics.uci.edu/~goodrich/teach/cs165/notes/WeakAVLTrees.pdf
-//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
#define LLVM_LIBC_SRC___SUPPORT_WEAK_AVL_H
@@ -24,6 +19,54 @@
namespace LIBC_NAMESPACE_DECL {
+// A general self-balancing binary search tree where the node pointer can
+// be used as stable handles to the stored values.
+//
+// The self-balancing strategy is the Weak AVL (WAVL) tree, based on the
+// following foundational references:
+// 1. https://maskray.me/blog/2025-12-14-weak-avl-tree
+// 2. https://reviews.freebsd.org/D25480
+// 3. https://ics.uci.edu/~goodrich/teach/cs165/notes/WeakAVLTrees.pdf
+// 4. https://dl.acm.org/doi/10.1145/2689412 (Rank-Balanced Trees)
+//
+// WAVL trees belong to the rank-balanced binary search tree framework
+// (reference 4), alongside AVL and Red-Black trees.
+//
+// Key Properties of WAVL Trees:
+// 1. Relationship to Red-Black Trees: A WAVL tree can always be colored as a
+// Red-Black tree.
+// 2. Relationship to AVL Trees: An AVL tree meets all the requirements of a
+// WAVL tree. Insertion-only WAVL trees maintain the same structure as AVL
+// trees.
+//
+// Rank-Based Balancing:
+// In rank-balanced trees, each node is assigned a rank (conceptually similar
+// to height). The rank difference between a parent and its child is
+// strictly enforced to be either **1** or **2**.
+//
+// - **AVL Trees:** Rank is equivalent to height. The strict condition is that
+// there are no 2-2 nodes (a parent with rank difference 2 to both children).
+// - **WAVL Trees:** The no 2-2 node rule is relaxed for internal nodes during
+// the deletion fixup process, making WAVL trees less strictly balanced than
+// AVL trees but easier to maintain than Red-Black trees.
+//
+// Balancing Mechanics (Promotion/Demotion):
+// - **Null nodes** are considered to have rank -1.
+// - **External/leaf nodes** have rank 0.
+// - **Insertion:** Inserting a node may create a situation where a parent and
+// child
+// have the same rank (difference 0). This is fixed by **promoting** the rank
+// of the parent and propagating the fix upwards using at most two rotations
+// (trinode fixup).
+// - **Deletion:** Deleting a node may result in a parent being 3 ranks higher
+// than a child (difference 3). This is fixed by **demoting** the parent's
+// rank and propagating the fix upwards.
+//
+// Implementation Detail:
+// The rank is **implicitly** maintained. We never store the full rank. Instead,
+// a 2-bit tag is used on each node to record the rank difference to each child:
+// - Bit cleared (0) -> Rank difference is **1**.
+// - Bit set (1) -> Rank difference is **2**.
template <typename T> class WeakAVLNode {
// Data
T data;
>From d5c94d092d801f2d18180cf52ee048e67f1b2508 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 17 Dec 2025 04:48:37 -0500
Subject: [PATCH 17/20] separate flag and parent pointer
---
libc/src/__support/weak_avl.h | 154 +++++++++++-----------
libc/test/src/__support/weak_avl_test.cpp | 4 +-
2 files changed, 82 insertions(+), 76 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 2b960b7febae2..73746ffb20f7f 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -71,44 +71,48 @@ template <typename T> class WeakAVLNode {
// Data
T data;
- // Packs the parent pointer with 2 flag bits in the low bits. Bit 0 indicates
- // whether the left child has rank difference 2; bit 1 indicates whether the
- // right child has rank difference 2. A cleared bit means rank difference 1.
- // All rank differences are 1 or 2, and every leaf has rank 0.
- uintptr_t parent_and_flags;
+ // Parent pointer
+ WeakAVLNode *parent;
// Children pointers
WeakAVLNode *children[2];
- // Constants
- static LIBC_INLINE_VAR constexpr uintptr_t FLAGS_MASK = 0b11;
- static LIBC_INLINE_VAR constexpr uintptr_t LEFT_FLAG_BIT = 0b01;
- static LIBC_INLINE_VAR constexpr uintptr_t RIGHT_FLAG_BIT = 0b10;
+ // Flags
+ unsigned char left_rank_diff_2 : 1;
+ unsigned char right_rank_diff_2 : 1;
- // Auxiliary methods for accessing fields
- LIBC_INLINE WeakAVLNode *parent() const {
- return cpp::bit_cast<WeakAVLNode *>(parent_and_flags & ~FLAGS_MASK);
+ LIBC_INLINE bool is_leaf() {
+ return (children[0] == nullptr) && (children[1] == nullptr);
}
- LIBC_INLINE uintptr_t flags() const { return parent_and_flags & FLAGS_MASK; }
- LIBC_INLINE void set_parent(WeakAVLNode *p) {
- parent_and_flags = cpp::bit_cast<uintptr_t>(p) | flags();
+
+ LIBC_INLINE void toggle_rank_diff_2(bool is_right) {
+ if (is_right)
+ right_rank_diff_2 ^= 1;
+ else
+ left_rank_diff_2 ^= 1;
}
- LIBC_INLINE bool has_rank_diff_2(bool is_right) const {
- return flags() & (is_right ? RIGHT_FLAG_BIT : LEFT_FLAG_BIT);
+
+ LIBC_INLINE bool both_flags_set() const {
+ return left_rank_diff_2 && right_rank_diff_2;
}
- LIBC_INLINE void toggle_rank_diff_2(bool is_right) {
- parent_and_flags ^= (is_right ? RIGHT_FLAG_BIT : LEFT_FLAG_BIT);
+
+ LIBC_INLINE bool any_flag_set() const {
+ return left_rank_diff_2 || right_rank_diff_2;
}
- LIBC_INLINE void clear_flags() { parent_and_flags &= ~FLAGS_MASK; }
- LIBC_INLINE void set_flags(uintptr_t flags) {
- parent_and_flags = (parent_and_flags & ~FLAGS_MASK) | (flags & FLAGS_MASK);
+
+ LIBC_INLINE void clear_flags() {
+ left_rank_diff_2 = 0;
+ right_rank_diff_2 = 0;
}
- LIBC_INLINE bool is_leaf() {
- return (children[0] == nullptr) && (children[1] == nullptr);
+
+ LIBC_INLINE void set_both_flags() {
+ left_rank_diff_2 = 1;
+ right_rank_diff_2 = 1;
}
+
LIBC_INLINE WeakAVLNode(T data)
- : data(cpp::move(data)), parent_and_flags(0), children{nullptr, nullptr} {
- }
+ : data(cpp::move(data)), parent(nullptr), children{nullptr, nullptr},
+ left_rank_diff_2(0), right_rank_diff_2(0) {}
LIBC_INLINE static WeakAVLNode *create(T value) {
AllocChecker ac;
@@ -135,11 +139,11 @@ template <typename T> class WeakAVLNode {
// Case 0: no children
if (!has_left && !has_right) {
- if (!node->parent()) {
+ if (!node->parent) {
root = nullptr;
return {nullptr, false};
}
- FixupSite site = {node->parent(), node->parent()->children[1] == node};
+ FixupSite site = {node->parent, node->parent->children[1] == node};
site.parent->children[site.is_right] = nullptr;
return site;
}
@@ -147,14 +151,14 @@ template <typename T> class WeakAVLNode {
// Case 1: one child
if (has_left != has_right) {
WeakAVLNode *child = node->children[has_right];
- if (!node->parent()) {
+ if (!node->parent) {
root = child;
- child->set_parent(nullptr);
+ child->parent = nullptr;
return {nullptr, false};
}
- FixupSite site = {node->parent(), node->parent()->children[1] == node};
+ FixupSite site = {node->parent, node->parent->children[1] == node};
site.parent->children[site.is_right] = child;
- child->set_parent(site.parent);
+ child->parent = site.parent;
return site;
}
@@ -163,7 +167,7 @@ template <typename T> class WeakAVLNode {
while (succ->children[0])
succ = succ->children[0];
- WeakAVLNode *succ_parent = succ->parent();
+ WeakAVLNode *succ_parent = succ->parent;
bool succ_was_right =
succ_parent->children[1] == succ; // true only if succ_parent==node
WeakAVLNode *succ_rchild = succ->children[1];
@@ -173,23 +177,23 @@ template <typename T> class WeakAVLNode {
FixupSite site = {succ_parent, succ_was_right};
succ_parent->children[succ_was_right] = succ_rchild;
if (succ_rchild)
- succ_rchild->set_parent(succ_parent);
+ succ_rchild->parent = succ_parent;
// 2) Transplant successor into node's position
- succ->set_parent(node->parent());
- succ->set_flags(
- node->flags()); // key-swap emulation: successor takes node's rank/flags
+ succ->parent = node->parent;
+ succ->left_rank_diff_2 = node->left_rank_diff_2;
+ succ->right_rank_diff_2 = node->right_rank_diff_2;
succ->children[0] = node->children[0];
succ->children[1] = node->children[1];
if (succ->children[0])
- succ->children[0]->set_parent(succ);
+ succ->children[0]->parent = succ;
if (succ->children[1])
- succ->children[1]->set_parent(succ);
+ succ->children[1]->parent = succ;
- if (succ->parent()) {
- bool node_was_right = succ->parent()->children[1] == node;
- succ->parent()->children[node_was_right] = succ;
+ if (succ->parent) {
+ bool node_was_right = succ->parent->children[1] == node;
+ succ->parent->children[node_was_right] = succ;
} else
root = succ;
@@ -206,8 +210,8 @@ template <typename T> class WeakAVLNode {
LIBC_INLINE const WeakAVLNode *get_left() const { return children[0]; }
LIBC_INLINE const WeakAVLNode *get_right() const { return children[1]; }
LIBC_INLINE const T &get_data() const { return data; }
- LIBC_INLINE bool is_rank_diff_2(bool is_right) const {
- return has_rank_diff_2(is_right);
+ LIBC_INLINE bool has_rank_diff_2(bool is_right) const {
+ return is_right ? right_rank_diff_2 : left_rank_diff_2;
}
// Destroy the subtree rooted at node
@@ -235,17 +239,17 @@ template <typename T> class WeakAVLNode {
WeakAVLNode *grandchild = pivot->children[!is_right];
node->children[is_right] = grandchild;
if (grandchild)
- grandchild->set_parent(node);
- pivot->set_parent(node->parent());
+ grandchild->parent = node;
+ pivot->parent = node->parent;
// Pivot becomes the new root of the subtree
- if (!node->parent())
+ if (!node->parent)
root = pivot;
else {
- bool node_is_right = node->parent()->children[1] == node;
- node->parent()->children[node_is_right] = pivot;
+ bool node_is_right = node->parent->children[1] == node;
+ node->parent->children[node_is_right] = pivot;
}
pivot->children[!is_right] = node;
- node->set_parent(pivot);
+ node->parent = pivot;
return pivot;
}
@@ -286,7 +290,7 @@ template <typename T> class WeakAVLNode {
if (!allocated)
return nullptr;
WeakAVLNode *node = allocated;
- node->set_parent(parent);
+ node->parent = parent;
// Case 0: inserting into an empty tree
if (!parent) {
@@ -325,14 +329,14 @@ template <typename T> class WeakAVLNode {
if (!sibling_has_rank_diff_2) {
parent->toggle_rank_diff_2(!is_right);
node = parent;
- parent = node->parent();
+ parent = node->parent;
continue;
}
- LIBC_ASSERT((node->flags() != 0b11) &&
+ LIBC_ASSERT(!node->both_flags_set() &&
"there should be no 2-2 node along the insertion fixup path");
- LIBC_ASSERT((node == allocated || node->flags() != 0) &&
+ LIBC_ASSERT((node == allocated || node->any_flag_set()) &&
"Internal node must have a child with rank-difference 2, "
"otherwise it should have already been handled.");
@@ -375,7 +379,8 @@ template <typename T> class WeakAVLNode {
rotate(root, parent, is_right); // Second rotation
LIBC_ASSERT(subroot1 == subroot2 &&
"Subroots after double rotation should be the same");
- uintptr_t flags = subroot1->flags();
+ bool subroot_left_diff_2 = subroot1->left_rank_diff_2;
+ bool subroot_right_diff_2 = subroot1->right_rank_diff_2;
node->clear_flags();
parent->clear_flags();
subroot1->clear_flags();
@@ -383,10 +388,10 @@ template <typename T> class WeakAVLNode {
WeakAVLNode *dst_left = is_right ? parent : node;
WeakAVLNode *dst_right = is_right ? node : parent;
// Masked toggles
- if (flags & LEFT_FLAG_BIT)
+ if (subroot_left_diff_2)
dst_left->toggle_rank_diff_2(true);
- if (flags & RIGHT_FLAG_BIT)
+ if (subroot_right_diff_2)
dst_right->toggle_rank_diff_2(false);
break;
}
@@ -411,11 +416,11 @@ template <typename T> class WeakAVLNode {
if (!cursor->has_rank_diff_2(is_right)) {
cursor->toggle_rank_diff_2(is_right);
// If we created a 2-2 leaf, we must demote it and continue.
- if (cursor->flags() == 0b11 && cursor->is_leaf()) {
+ if (cursor->both_flags_set() && cursor->is_leaf()) {
cursor->clear_flags();
- if (cursor->parent())
- is_right = (cursor->parent()->children[1] == cursor);
- cursor = cursor->parent();
+ if (cursor->parent)
+ is_right = (cursor->parent->children[1] == cursor);
+ cursor = cursor->parent;
continue;
}
break;
@@ -435,9 +440,9 @@ template <typename T> class WeakAVLNode {
// (D) (D)
if (cursor->has_rank_diff_2(!is_right)) {
cursor->toggle_rank_diff_2(!is_right);
- if (cursor->parent())
- is_right = (cursor->parent()->children[1] == cursor);
- cursor = cursor->parent();
+ if (cursor->parent)
+ is_right = (cursor->parent->children[1] == cursor);
+ cursor = cursor->parent;
continue;
}
@@ -455,11 +460,11 @@ template <typename T> class WeakAVLNode {
// (*) (*) (*) (*)
WeakAVLNode *sibling = cursor->children[!is_right];
LIBC_ASSERT(sibling && "rank-difference 1 sibling cannot be empty");
- if (sibling->flags() == 0b11) {
+ if (sibling->both_flags_set()) {
sibling->clear_flags();
- if (cursor->parent())
- is_right = (cursor->parent()->children[1] == cursor);
- cursor = cursor->parent();
+ if (cursor->parent)
+ is_right = (cursor->parent->children[1] == cursor);
+ cursor = cursor->parent;
continue;
}
@@ -491,11 +496,11 @@ template <typename T> class WeakAVLNode {
if (cursor->is_leaf()) {
cursor->clear_flags();
new_subroot->toggle_rank_diff_2(!sibling_is_right);
- LIBC_ASSERT(new_subroot->flags() == 0b11 &&
+ LIBC_ASSERT(new_subroot->both_flags_set() &&
"sibling should become a 2-2 node.");
} else {
cursor->toggle_rank_diff_2(sibling_is_right);
- LIBC_ASSERT(cursor->flags() == 0b11 &&
+ LIBC_ASSERT(cursor->both_flags_set() &&
"cursor should become a 2-2 node.");
}
}
@@ -514,21 +519,22 @@ template <typename T> class WeakAVLNode {
// (*) Y / \ Z (*) (A)(B) (D)
// (A) (B)
WeakAVLNode *target_child = rotate(root, sibling, !sibling_is_right);
- uintptr_t flags = target_child->flags();
+ bool subtree_left_diff_2 = target_child->left_rank_diff_2;
+ bool subtree_right_diff_2 = target_child->right_rank_diff_2;
WeakAVLNode *new_subroot = rotate(root, cursor, sibling_is_right);
LIBC_ASSERT(new_subroot == target_child &&
"target_child should become the subtree root");
// Set flags
- target_child->set_flags(0b11);
+ target_child->set_both_flags();
cursor->clear_flags();
sibling->clear_flags();
// Select destinations
WeakAVLNode *dst_left = sibling_is_right ? cursor : sibling;
WeakAVLNode *dst_right = sibling_is_right ? sibling : cursor;
// Masked toggles
- if (flags & LEFT_FLAG_BIT)
+ if (subtree_left_diff_2)
dst_left->toggle_rank_diff_2(true);
- if (flags & RIGHT_FLAG_BIT)
+ if (subtree_right_diff_2)
dst_right->toggle_rank_diff_2(false);
break;
}
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index 6f66b11f515a3..65e26408a3707 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -22,8 +22,8 @@ constexpr int TEST_SIZE = 128;
bool validate_pure_insertion(const Node *node) {
if (!node)
return true;
- bool left_2 = node->is_rank_diff_2(false);
- bool right_2 = node->is_rank_diff_2(true);
+ bool left_2 = node->has_rank_diff_2(false);
+ bool right_2 = node->has_rank_diff_2(true);
return (!left_2 || !right_2) && validate_pure_insertion(node->get_left()) &&
validate_pure_insertion(node->get_right());
}
>From b8f133c9abb25c6f92ca93a78edb456bed4bcebe Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 17 Dec 2025 18:30:04 -0500
Subject: [PATCH 18/20] simplify the code to address comments
---
libc/src/__support/weak_avl.h | 280 +++++++++++++++++-----------------
1 file changed, 144 insertions(+), 136 deletions(-)
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 73746ffb20f7f..2b2ad237c81b0 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -54,10 +54,9 @@ namespace LIBC_NAMESPACE_DECL {
// - **Null nodes** are considered to have rank -1.
// - **External/leaf nodes** have rank 0.
// - **Insertion:** Inserting a node may create a situation where a parent and
-// child
-// have the same rank (difference 0). This is fixed by **promoting** the rank
-// of the parent and propagating the fix upwards using at most two rotations
-// (trinode fixup).
+// child have the same rank (difference 0). This is fixed by **promoting**
+// the rank of the parent and propagating the fix upwards using at most two
+// rotations (trinode fixup).
// - **Deletion:** Deleting a node may result in a parent being 3 ranks higher
// than a child (difference 3). This is fixed by **demoting** the parent's
// rank and propagating the fix upwards.
@@ -116,19 +115,19 @@ template <typename T> class WeakAVLNode {
LIBC_INLINE static WeakAVLNode *create(T value) {
AllocChecker ac;
- WeakAVLNode *res = ::new (ac) WeakAVLNode(value);
+ WeakAVLNode *res = new (ac) WeakAVLNode(value);
if (ac)
return res;
return nullptr;
}
- // Unlink a node from tree. The corresponding flag is not update. The node is
+ // Unlink a node from tree. The corresponding flag is not updated. The node is
// not deleted and its pointers are not cleared.
// FixupSite is the lowest surviving node from which rank/flag invariants may
// be violated.
// Our tree requires value to stay in their node to maintain stable addresses.
// This complicates the unlink operation as the successor transplanting needs
- // to updates all the pointers and flags.
+ // to update all the pointers and flags.
struct FixupSite {
WeakAVLNode *parent;
bool is_right;
@@ -168,8 +167,9 @@ template <typename T> class WeakAVLNode {
succ = succ->children[0];
WeakAVLNode *succ_parent = succ->parent;
- bool succ_was_right =
- succ_parent->children[1] == succ; // true only if succ_parent==node
+ // succ and node may be adjacent to each other, so we
+ // still need to check the exact direction of the successor.
+ bool succ_was_right = succ_parent->children[1] == succ;
WeakAVLNode *succ_rchild = succ->children[1];
// 1) Splice successor out of its old position (flags intentionally
@@ -300,39 +300,41 @@ template <typename T> class WeakAVLNode {
parent->children[is_right] = node;
// Rebalance process
- while (parent) {
- is_right = (parent->children[1] == node);
- // Case 1: parent does not need to be promoted as node is lowering
- // than the parent by 2 ranks.
- // (P) (P)
- // / \ / \
- // 2 1 => 1 1
- // / \ / \
- // (N) (*) (N) (*)
- if (parent->has_rank_diff_2(is_right)) {
- parent->toggle_rank_diff_2(is_right);
- break;
- }
-
- bool sibling_has_rank_diff_2 = parent->has_rank_diff_2(!is_right);
- // Case 2: node's sibling has rank-difference 1.
- // Promoting parent will fix the conflict of the trinodes but we may need
- // to continue on parent.
- //
- // (GP) (GP)
- // | Promote | x - 1
- // | x -----> (P)
- // 0 | / 1 / \
- // (N) --- (P) ---- (N) \ 2
- // \ 1 \
- // (S) (S)
- if (!sibling_has_rank_diff_2) {
- parent->toggle_rank_diff_2(!is_right);
- node = parent;
- parent = node->parent;
- continue;
- }
-
+ // Case 1: both node and its sibling have rank-difference 1. So after the
+ // insertion, the node is at the same level as the parent. Promoting parent
+ // will fix the conflict of the trinodes but we may need to continue on
+ // parent.
+ //
+ // (GP) (GP)
+ // | Promote | x - 1
+ // | x -----> (P)
+ // 0 | / 1 / \
+ // (N) --- (P) ---- (N) \ 2
+ // \ 1 \
+ // (S) (S)
+ while (parent && !parent->any_flag_set()) {
+ parent->toggle_rank_diff_2(!is_right);
+ node = parent;
+ parent = node->parent;
+ if (parent)
+ is_right = (parent->children[1] == node);
+ continue;
+ }
+ // We finish if node has reaches the root -- otherwise, we end up with
+ // two more cases.
+ if (!parent)
+ return allocated;
+
+ // Case 2: parent does not need to be promoted as node is lower
+ // than the parent by 2 ranks.
+ // (P) (P)
+ // / \ / \
+ // 2 1 => 1 1
+ // / \ / \
+ // (N) (*) (N) (*)
+ if (parent->has_rank_diff_2(is_right))
+ parent->toggle_rank_diff_2(is_right);
+ else {
LIBC_ASSERT(!node->both_flags_set() &&
"there should be no 2-2 node along the insertion fixup path");
@@ -354,7 +356,6 @@ template <typename T> class WeakAVLNode {
WeakAVLNode *new_subroot = rotate(root, parent, is_right);
new_subroot->clear_flags();
parent->clear_flags();
- break;
}
// Case 4: node's sibling has rank-difference 2. And node has a 1-node
// along the opposite direction. We need a double rotation to fix the
@@ -374,27 +375,29 @@ template <typename T> class WeakAVLNode {
// / (C1) \ (P) (N)
// (S) L / \ R (C2) 1 / \ L R / \ 1
// (A) (B) (S)(A) (B)(C2)
- WeakAVLNode *subroot1 = rotate(root, node, !is_right); // First rotation
- [[maybe_unused]] WeakAVLNode *subroot2 =
- rotate(root, parent, is_right); // Second rotation
- LIBC_ASSERT(subroot1 == subroot2 &&
- "Subroots after double rotation should be the same");
- bool subroot_left_diff_2 = subroot1->left_rank_diff_2;
- bool subroot_right_diff_2 = subroot1->right_rank_diff_2;
- node->clear_flags();
- parent->clear_flags();
- subroot1->clear_flags();
- // Select destinations
- WeakAVLNode *dst_left = is_right ? parent : node;
- WeakAVLNode *dst_right = is_right ? node : parent;
- // Masked toggles
- if (subroot_left_diff_2)
- dst_left->toggle_rank_diff_2(true);
-
- if (subroot_right_diff_2)
- dst_right->toggle_rank_diff_2(false);
- break;
+ else {
+ WeakAVLNode *subroot1 = rotate(root, node, !is_right); // First rotation
+ [[maybe_unused]] WeakAVLNode *subroot2 =
+ rotate(root, parent, is_right); // Second rotation
+ LIBC_ASSERT(subroot1 == subroot2 &&
+ "Subroots after double rotation should be the same");
+ bool subroot_left_diff_2 = subroot1->left_rank_diff_2;
+ bool subroot_right_diff_2 = subroot1->right_rank_diff_2;
+ node->clear_flags();
+ parent->clear_flags();
+ subroot1->clear_flags();
+ // Select destinations
+ WeakAVLNode *dst_left = is_right ? parent : node;
+ WeakAVLNode *dst_right = is_right ? node : parent;
+ // Masked toggles
+ if (subroot_left_diff_2)
+ dst_left->toggle_rank_diff_2(true);
+
+ if (subroot_right_diff_2)
+ dst_right->toggle_rank_diff_2(false);
+ }
}
+
return allocated;
}
@@ -402,7 +405,8 @@ template <typename T> class WeakAVLNode {
LIBC_INLINE static void erase(WeakAVLNode *&root, WeakAVLNode *node) {
// Unlink the node from the tree
auto [cursor, is_right] = unlink(root, node);
- ::delete node;
+ delete node;
+ WeakAVLNode *sibling = nullptr;
while (cursor) {
// Case 0. cursor previously had rank-difference 1 on the side of the
// deleted node. We can simply update the rank-difference and stop.
@@ -416,14 +420,12 @@ template <typename T> class WeakAVLNode {
if (!cursor->has_rank_diff_2(is_right)) {
cursor->toggle_rank_diff_2(is_right);
// If we created a 2-2 leaf, we must demote it and continue.
- if (cursor->both_flags_set() && cursor->is_leaf()) {
- cursor->clear_flags();
- if (cursor->parent)
- is_right = (cursor->parent->children[1] == cursor);
- cursor = cursor->parent;
- continue;
- }
- break;
+ // Otherwise, we are done as the internal node becomes a 2-2 node and
+ // there is no further violation upwards.
+ if (!cursor->both_flags_set() || !cursor->is_leaf())
+ return;
+ // Clear flags for demotion.
+ cursor->clear_flags();
}
// Case 1. cursor previously had rank-difference 2 on the side of the
@@ -438,17 +440,13 @@ template <typename T> class WeakAVLNode {
// 2 / \ 1 / \
// (*) \ 3 (*) \ 2
// (D) (D)
- if (cursor->has_rank_diff_2(!is_right)) {
+ else if (cursor->has_rank_diff_2(!is_right))
cursor->toggle_rank_diff_2(!is_right);
- if (cursor->parent)
- is_right = (cursor->parent->children[1] == cursor);
- cursor = cursor->parent;
- continue;
- }
// Case 2. continue from Case 1; but the sibling has rank-difference 1.
// However, we found that the sibling is a 2-2 node. We demote both
- // sibling and cursor, and continue upwards.
+ // sibling and cursor, and continue upwards. We break for other cases if
+ // sibling cannot be demoted.
//
// (P) (P)
// | X | (X + 1)
@@ -458,66 +456,77 @@ template <typename T> class WeakAVLNode {
// / \ \ 3 (S) \ 2
// 2 / \ 2 (D) 1 / \ 1 (D)
// (*) (*) (*) (*)
- WeakAVLNode *sibling = cursor->children[!is_right];
- LIBC_ASSERT(sibling && "rank-difference 1 sibling cannot be empty");
- if (sibling->both_flags_set()) {
- sibling->clear_flags();
- if (cursor->parent)
- is_right = (cursor->parent->children[1] == cursor);
- cursor = cursor->parent;
- continue;
+ else {
+ sibling = cursor->children[!is_right];
+ LIBC_ASSERT(sibling && "rank-difference 1 sibling cannot be empty");
+ if (sibling->both_flags_set())
+ sibling->clear_flags();
+ else
+ break;
}
- // Case 3. continue from Case 2; but the sibling cannot be demoted.
- // Sibling has a node T along the same direction with rank-difference 1.
- //
- // (P) (P)
- // | X | X
- // (C) (S)
- // 1 / \ Rotate 2 / \ 1
- // (S) \ => / (C)
- // 1 / \ Y \ 3 (T) Y / \ 2
- // (T) \ (D) (*) \
- // (*) (D)
- bool sibling_is_right = !is_right;
- if (!sibling->has_rank_diff_2(sibling_is_right)) {
- WeakAVLNode *new_subroot = rotate(root, cursor, sibling_is_right);
- LIBC_ASSERT(new_subroot == sibling &&
- "sibling should become the subtree root");
- // Update flags
- bool sibling_alter_child_has_rank_diff_2 =
- new_subroot->has_rank_diff_2(!sibling_is_right);
- new_subroot->clear_flags();
- new_subroot->toggle_rank_diff_2(sibling_is_right);
-
- // Cursor only needs to be updated if it become a 2-2 node
- if (sibling_alter_child_has_rank_diff_2) {
- // Demote a 2-2 cursor if it is a leaf
- if (cursor->is_leaf()) {
- cursor->clear_flags();
- new_subroot->toggle_rank_diff_2(!sibling_is_right);
- LIBC_ASSERT(new_subroot->both_flags_set() &&
- "sibling should become a 2-2 node.");
- } else {
- cursor->toggle_rank_diff_2(sibling_is_right);
- LIBC_ASSERT(cursor->both_flags_set() &&
- "cursor should become a 2-2 node.");
- }
+ // Update cursor to move upwards
+ if (cursor->parent)
+ is_right = (cursor->parent->children[1] == cursor);
+ cursor = cursor->parent;
+ }
+
+ // Either cursor is nullptr (we reached the root), or sibling has
+ // rank-difference 1.
+ if (!cursor)
+ return;
+ LIBC_ASSERT(sibling && "rank-difference 1 sibling must exist");
+ bool sibling_is_right = !is_right; // Rename for clarity
+
+ // Case 3. continue from Case 2; but the sibling cannot be demoted.
+ // Sibling has a node T along the same direction with rank-difference 1.
+ //
+ // (P) (P)
+ // | X | X
+ // (C) (S)
+ // 1 / \ Rotate 2 / \ 1
+ // (S) \ => / (C)
+ // 1 / \ Y \ 3 (T) Y / \ 2
+ // (T) \ (D) (*) \
+ // (*) (D)
+ if (!sibling->has_rank_diff_2(sibling_is_right)) {
+ WeakAVLNode *new_subroot = rotate(root, cursor, sibling_is_right);
+ LIBC_ASSERT(new_subroot == sibling &&
+ "sibling should become the subtree root");
+ // Update flags
+ bool sibling_alter_child_has_rank_diff_2 =
+ new_subroot->has_rank_diff_2(!sibling_is_right);
+ new_subroot->clear_flags();
+ new_subroot->toggle_rank_diff_2(sibling_is_right);
+
+ // Cursor only needs to be updated if it become a 2-2 node
+ if (sibling_alter_child_has_rank_diff_2) {
+ // Demote a 2-2 cursor if it is a leaf
+ if (cursor->is_leaf()) {
+ cursor->clear_flags();
+ new_subroot->toggle_rank_diff_2(!sibling_is_right);
+ LIBC_ASSERT(new_subroot->both_flags_set() &&
+ "sibling should become a 2-2 node.");
+ } else {
+ cursor->toggle_rank_diff_2(sibling_is_right);
+ LIBC_ASSERT(cursor->both_flags_set() &&
+ "cursor should become a 2-2 node.");
}
- break;
}
- // Case 4. continue from Case 3; but rank-difference 1 child T of sibling
- // is on the opposite direction.
- //
- // (P) (P)
- // | X | X
- // (C) Zig-Zag (T)
- // 1 / \ => / \
- // (S) \ 2 / \ 2
- // / \ 1 \ 3 (S) (C)
- // 2 / (T) (D) 1 / Y \ / Z \ 1
- // (*) Y / \ Z (*) (A)(B) (D)
- // (A) (B)
+ }
+ // Case 4. continue from Case 3; but rank-difference 1 child T of sibling
+ // is on the opposite direction.
+ //
+ // (P) (P)
+ // | X | X
+ // (C) Zig-Zag (T)
+ // 1 / \ => / \
+ // (S) \ 2 / \ 2
+ // / \ 1 \ 3 (S) (C)
+ // 2 / (T) (D) 1 / Y \ / Z \ 1
+ // (*) Y / \ Z (*) (A)(B) (D)
+ // (A) (B)
+ else {
WeakAVLNode *target_child = rotate(root, sibling, !sibling_is_right);
bool subtree_left_diff_2 = target_child->left_rank_diff_2;
bool subtree_right_diff_2 = target_child->right_rank_diff_2;
@@ -536,7 +545,6 @@ template <typename T> class WeakAVLNode {
dst_left->toggle_rank_diff_2(true);
if (subtree_right_diff_2)
dst_right->toggle_rank_diff_2(false);
- break;
}
}
>From 069fb9b5c8a6416e58483d8c1e023be659da4ac8 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 16 Dec 2025 20:43:17 -0500
Subject: [PATCH 19/20] stage
---
libc/hdr/types/pthread_tnode.h | 28 +++++++++
libc/include/CMakeLists.txt | 1 +
libc/include/llvm-libc-types/CMakeLists.txt | 1 +
libc/include/llvm-libc-types/posix_tnode.h | 17 ++++++
libc/include/search.yaml | 45 ++++++++++++++
libc/src/search/CMakeLists.txt | 66 +++++++++++++++++++++
libc/src/search/tdelete.cpp | 25 ++++++++
libc/src/search/tdelete.h | 20 +++++++
libc/src/search/tdestroy.cpp | 22 +++++++
libc/src/search/tdestroy.h | 19 ++++++
libc/src/search/tfind.cpp | 25 ++++++++
libc/src/search/tfind.h | 20 +++++++
libc/src/search/tsearch.cpp | 25 ++++++++
libc/src/search/tsearch.h | 20 +++++++
libc/src/search/twalk.cpp | 23 +++++++
libc/src/search/twalk.h | 20 +++++++
libc/src/search/twalk_r.cpp | 25 ++++++++
libc/src/search/twalk_r.h | 20 +++++++
18 files changed, 422 insertions(+)
create mode 100644 libc/hdr/types/pthread_tnode.h
create mode 100644 libc/include/llvm-libc-types/posix_tnode.h
create mode 100644 libc/src/search/tdelete.cpp
create mode 100644 libc/src/search/tdelete.h
create mode 100644 libc/src/search/tdestroy.cpp
create mode 100644 libc/src/search/tdestroy.h
create mode 100644 libc/src/search/tfind.cpp
create mode 100644 libc/src/search/tfind.h
create mode 100644 libc/src/search/tsearch.cpp
create mode 100644 libc/src/search/tsearch.h
create mode 100644 libc/src/search/twalk.cpp
create mode 100644 libc/src/search/twalk.h
create mode 100644 libc/src/search/twalk_r.cpp
create mode 100644 libc/src/search/twalk_r.h
diff --git a/libc/hdr/types/pthread_tnode.h b/libc/hdr/types/pthread_tnode.h
new file mode 100644
index 0000000000000..9c222bdccbb3b
--- /dev/null
+++ b/libc/hdr/types/pthread_tnode.h
@@ -0,0 +1,28 @@
+//===-- Definition of macros from pthread_barrierattr_t.h -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
+#define LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
+
+// posix_tnode is introduced in POSIX.1-2024.
+// this may result in problems when overlaying on older systems.
+// internal code should use __llvm_libc_tnode.
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/pthread_tnode.h"
+#define __llvm_libc_tnode posix_tnode
+
+#else // Overlay mode
+
+#include <search.h>
+typedef void __llvm_libc_tnode;
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 96c3a88f3fcc8..5d5aff435c5fc 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -273,6 +273,7 @@ add_header_macro(
.llvm-libc-types.ACTION
.llvm-libc-types.ENTRY
.llvm-libc-types.VISIT
+ .llvm-libc-types.posix_tnode
.llvm-libc-types.__search_compare_t
.llvm-libc-types.size_t
.llvm-libc-types.struct_hsearch_data
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index fcd0e1245a4a8..309f671f40bba 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -52,6 +52,7 @@ add_header(off_t HDR off_t.h)
add_header(once_flag HDR once_flag.h DEPENDS .__futex_word)
add_header(posix_spawn_file_actions_t HDR posix_spawn_file_actions_t.h)
add_header(posix_spawnattr_t HDR posix_spawnattr_t.h)
+add_header(posix_tnode HDR posix_tnode.h)
add_header(pthread_attr_t HDR pthread_attr_t.h DEPENDS .size_t)
add_header(pthread_condattr_t HDR pthread_condattr_t.h DEPENDS .clockid_t)
add_header(pthread_key_t HDR pthread_key_t.h)
diff --git a/libc/include/llvm-libc-types/posix_tnode.h b/libc/include/llvm-libc-types/posix_tnode.h
new file mode 100644
index 0000000000000..0f7336ac2ad89
--- /dev/null
+++ b/libc/include/llvm-libc-types/posix_tnode.h
@@ -0,0 +1,17 @@
+//===-- Definition of type posix_tnode--------------- ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_POSIX_TNODE_H
+#define LLVM_LIBC_TYPES_POSIX_TNODE_H
+
+// Following POSIX.1-2024 and Austin Group Defect Report 1011:
+// https://austingroupbugs.net/view.php?id=1011
+// Define posix_tnode as void to provide both type safety and compatibility.
+typedef void posix_tnode;
+
+#endif // LLVM_LIBC_TYPES_POSIX_TNODE_H
diff --git a/libc/include/search.yaml b/libc/include/search.yaml
index 8a3a0c50af60f..864c96503d49b 100644
--- a/libc/include/search.yaml
+++ b/libc/include/search.yaml
@@ -7,6 +7,7 @@ types:
- type_name: VISIT
- type_name: __search_compare_t
- type_name: struct_hsearch_data
+ - type_name: posix_tnode
enums: []
objects: []
functions:
@@ -80,3 +81,47 @@ functions:
- type: size_t *
- type: size_t
- type: __search_compare_t
+ - name: tdelete
+ standards:
+ - posix
+ return_type: void *
+ arguments:
+ - type: const void *__restrict
+ - type: posix_tnode **__restrict
+ - type: __search_compare_t
+ - name: tfind
+ standards:
+ - posix
+ return_type: posix_tnode *
+ arguments:
+ - type: const void *
+ - type: posix_tnode * const *
+ - type: __search_compare_t
+ - name: tsearch
+ standards:
+ - posix
+ return_type: posix_tnode *
+ arguments:
+ - type: const void *
+ - type: posix_tnode **
+ - type: __search_compare_t
+ - name: twalk
+ standards:
+ - posix
+ return_type: void
+ arguments:
+ - type: const posix_tnode *
+ - type: void (*)(const posix_tnode *, VISIT, int)
+ - name: twalk_r
+ standards: gnu
+ return_type: void
+ arguments:
+ - type: const posix_tnode *
+ - type: void (*)(const posix_tnode *, VISIT, void *)
+ - type: void *
+ - name: tdestroy
+ standards: gnu
+ return_type: void
+ arguments:
+ - type: posix_tnode *
+ - type: void (*)(void *)
diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt
index 0ed513e648ed1..fdb79d1be27a5 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -125,3 +125,69 @@ add_entrypoint_object(
libc.src.__support.memory_size
libc.src.string.memory_utils.inline_memcpy
)
+
+add_entrypoint_object(
+ tdelete
+ SRCS
+ tdelete.cpp
+ HDRS
+ tdelete.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+ tfind
+ SRCS
+ tfind.cpp
+ HDRS
+ tfind.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+ tsearch
+ SRCS
+ tsearch.cpp
+ HDRS
+ tsearch.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+ twalk
+ SRCS
+ twalk.cpp
+ HDRS
+ twalk.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+ twalk_r
+ SRCS
+ twalk_r.cpp
+ HDRS
+ twalk_r.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+ tdestroy
+ SRCS
+ tdestroy.cpp
+ HDRS
+ tdestroy.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.weak_avl
+)
diff --git a/libc/src/search/tdelete.cpp b/libc/src/search/tdelete.cpp
new file mode 100644
index 0000000000000..3a1dbbb0e89aa
--- /dev/null
+++ b/libc/src/search/tdelete.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of tdelete -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/tdelete.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void *, tdelete,
+ (const void *key, posix_tnode **rootp,
+ int (*compar)(const void *, const void *))) {
+ (void)key;
+ (void)rootp;
+ (void)compar;
+ return nullptr;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tdelete.h b/libc/src/search/tdelete.h
new file mode 100644
index 0000000000000..7e4f4a315cb27
--- /dev/null
+++ b/libc/src/search/tdelete.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for tdelete -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TDELETE_H
+#define LLVM_LIBC_SRC_SEARCH_TDELETE_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+void *tdelete(const void *key, posix_tnode **rootp,
+ int (*compar)(const void *, const void *));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TDELETE_H
diff --git a/libc/src/search/tdestroy.cpp b/libc/src/search/tdestroy.cpp
new file mode 100644
index 0000000000000..024069d4cb951
--- /dev/null
+++ b/libc/src/search/tdestroy.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of tdestroy ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/tdestroy.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, tdestroy,
+ (posix_tnode * root, void (*free_node)(void *))) {
+ (void)root;
+ (void)free_node;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tdestroy.h b/libc/src/search/tdestroy.h
new file mode 100644
index 0000000000000..6290e1da634f3
--- /dev/null
+++ b/libc/src/search/tdestroy.h
@@ -0,0 +1,19 @@
+//===-- Implementation header for tdestroy ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TDESTROY_H
+#define LLVM_LIBC_SRC_SEARCH_TDESTROY_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+void tdestroy(posix_tnode *root, void (*free_node)(void *));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TDESTROY_H
diff --git a/libc/src/search/tfind.cpp b/libc/src/search/tfind.cpp
new file mode 100644
index 0000000000000..dd1da93f8478c
--- /dev/null
+++ b/libc/src/search/tfind.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of tfind ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/tfind.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(posix_tnode *, tfind,
+ (const void *key, posix_tnode *const *rootp,
+ int (*compar)(const void *, const void *))) {
+ (void)key;
+ (void)rootp;
+ (void)compar;
+ return nullptr;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tfind.h b/libc/src/search/tfind.h
new file mode 100644
index 0000000000000..aea252bf4195b
--- /dev/null
+++ b/libc/src/search/tfind.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for tfind -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TFIND_H
+#define LLVM_LIBC_SRC_SEARCH_TFIND_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+posix_tnode *tfind(const void *key, posix_tnode *const *rootp,
+ int (*compar)(const void *, const void *));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TFIND_H
diff --git a/libc/src/search/tsearch.cpp b/libc/src/search/tsearch.cpp
new file mode 100644
index 0000000000000..5b974a29bf8d5
--- /dev/null
+++ b/libc/src/search/tsearch.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of tsearch -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/tsearch.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(posix_tnode *, tsearch,
+ (const void *key, posix_tnode **rootp,
+ int (*compar)(const void *, const void *))) {
+ (void)key;
+ (void)rootp;
+ (void)compar;
+ return nullptr;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tsearch.h b/libc/src/search/tsearch.h
new file mode 100644
index 0000000000000..565d852b019db
--- /dev/null
+++ b/libc/src/search/tsearch.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for tsearch -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TSEARCH_H
+#define LLVM_LIBC_SRC_SEARCH_TSEARCH_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+posix_tnode *tsearch(const void *key, posix_tnode **rootp,
+ int (*compar)(const void *, const void *));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TSEARCH_H
diff --git a/libc/src/search/twalk.cpp b/libc/src/search/twalk.cpp
new file mode 100644
index 0000000000000..5b655e8beaeaa
--- /dev/null
+++ b/libc/src/search/twalk.cpp
@@ -0,0 +1,23 @@
+//===-- Implementation of twalk --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/twalk.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, twalk,
+ (const posix_tnode *root,
+ void (*action)(const posix_tnode *, VISIT, int))) {
+ (void)root;
+ (void)action;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/twalk.h b/libc/src/search/twalk.h
new file mode 100644
index 0000000000000..ff599bbfbfbed
--- /dev/null
+++ b/libc/src/search/twalk.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for twalk -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TWALK_H
+#define LLVM_LIBC_SRC_SEARCH_TWALK_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+void twalk(const posix_tnode *root,
+ void (*action)(const posix_tnode *, VISIT, int));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TWALK_H
diff --git a/libc/src/search/twalk_r.cpp b/libc/src/search/twalk_r.cpp
new file mode 100644
index 0000000000000..47a741ed7723b
--- /dev/null
+++ b/libc/src/search/twalk_r.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of twalk_r ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/twalk_r.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/weak_avl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, twalk_r,
+ (const posix_tnode *root,
+ void (*action)(const posix_tnode *, VISIT, void *),
+ void *closure)) {
+ (void)root;
+ (void)action;
+ (void)closure;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/twalk_r.h b/libc/src/search/twalk_r.h
new file mode 100644
index 0000000000000..6812bcea4995f
--- /dev/null
+++ b/libc/src/search/twalk_r.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for twalk_r -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SEARCH_TWALK_R_H
+#define LLVM_LIBC_SRC_SEARCH_TWALK_R_H
+
+#include "src/__support/macros/config.h"
+#include <search.h> // posix_tnode, VISIT
+
+namespace LIBC_NAMESPACE_DECL {
+void twalk_r(const posix_tnode *root,
+ void (*action)(const posix_tnode *, VISIT, void *), void *closure);
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_TWALK_R_H
>From 77cf68376f2a1ad300b008f0b70238a3b5041fd8 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 17 Dec 2025 05:56:42 -0500
Subject: [PATCH 20/20] stage
---
libc/config/linux/aarch64/entrypoints.txt | 6 ++++
libc/config/linux/x86_64/entrypoints.txt | 6 ++++
libc/hdr/types/CMakeLists.txt | 18 +++++++++++
libc/hdr/types/VISIT.h | 22 ++++++++++++++
.../types/{pthread_tnode.h => posix_tnode.h} | 10 +++----
libc/src/__support/weak_avl.h | 30 +++++++++++++------
libc/src/search/CMakeLists.txt | 14 +++++----
libc/src/search/tdelete.cpp | 19 +++++++++---
libc/src/search/tdelete.h | 4 +--
libc/src/search/tdestroy.cpp | 12 ++++++--
libc/src/search/tdestroy.h | 4 +--
libc/src/search/tfind.cpp | 14 +++++----
libc/src/search/tfind.h | 6 ++--
libc/src/search/tsearch.cpp | 14 +++++----
libc/src/search/tsearch.h | 6 ++--
libc/src/search/twalk.cpp | 19 +++++++++---
libc/src/search/twalk.h | 7 +++--
libc/src/search/twalk_r.cpp | 19 ++++++++----
libc/src/search/twalk_r.h | 8 +++--
libc/test/src/__support/weak_avl_test.cpp | 11 +++----
20 files changed, 180 insertions(+), 69 deletions(-)
create mode 100644 libc/hdr/types/VISIT.h
rename libc/hdr/types/{pthread_tnode.h => posix_tnode.h} (71%)
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 970c825bbfc96..f2a634ea4afa1 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1116,6 +1116,12 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.lfind
libc.src.search.lsearch
libc.src.search.remque
+ libc.src.search.tdelete
+ libc.src.search.tdestroy
+ libc.src.search.tfind
+ libc.src.search.tsearch
+ libc.src.search.twalk
+ libc.src.search.twalk_r
# threads.h entrypoints
libc.src.threads.call_once
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 9399b284fa2da..a6ce8a3715d51 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1298,6 +1298,12 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.lfind
libc.src.search.lsearch
libc.src.search.remque
+ libc.src.search.tdelete
+ libc.src.search.tdestroy
+ libc.src.search.tfind
+ libc.src.search.tsearch
+ libc.src.search.twalk
+ libc.src.search.twalk_r
# threads.h entrypoints
libc.src.threads.call_once
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 433c47b174766..aed50e64aa2df 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -487,3 +487,21 @@ add_proxy_header_library(
FULL_BUILD_DEPENDS
libc.include.llvm-libc-types.gid_t
)
+
+add_proxy_header_library(
+ posix_tnode
+ HDRS
+ posix_tnode.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.posix_tnode
+ libc.include.search
+)
+
+add_proxy_header_library(
+ VISIT
+ HDRS
+ VISIT.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.VISIT
+ libc.include.search
+)
diff --git a/libc/hdr/types/VISIT.h b/libc/hdr/types/VISIT.h
new file mode 100644
index 0000000000000..e8377c233267c
--- /dev/null
+++ b/libc/hdr/types/VISIT.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from VISIT -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_VISIT_H
+#define LLVM_LIBC_HDR_TYPES_VISIT_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/VISIT.h"
+
+#else // Overlay mode
+
+#include <search.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_VISIT_H
diff --git a/libc/hdr/types/pthread_tnode.h b/libc/hdr/types/posix_tnode.h
similarity index 71%
rename from libc/hdr/types/pthread_tnode.h
rename to libc/hdr/types/posix_tnode.h
index 9c222bdccbb3b..0b2ecfd488217 100644
--- a/libc/hdr/types/pthread_tnode.h
+++ b/libc/hdr/types/posix_tnode.h
@@ -1,4 +1,4 @@
-//===-- Definition of macros from pthread_barrierattr_t.h -----------------===//
+//===-- Definition of macros from posix_tnode -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
-#define LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
+#ifndef LLVM_LIBC_HDR_TYPES_POSIX_TNODE_H
+#define LLVM_LIBC_HDR_TYPES_POSIX_TNODE_H
// posix_tnode is introduced in POSIX.1-2024.
// this may result in problems when overlaying on older systems.
@@ -15,7 +15,7 @@
#ifdef LIBC_FULL_BUILD
-#include "include/llvm-libc-types/pthread_tnode.h"
+#include "include/llvm-libc-types/posix_tnode.h"
#define __llvm_libc_tnode posix_tnode
#else // Overlay mode
@@ -25,4 +25,4 @@ typedef void __llvm_libc_tnode;
#endif // LLVM_LIBC_FULL_BUILD
-#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_TNODE_H
+#endif // LLVM_LIBC_HDR_TYPES_POSIX_TNODE_H
diff --git a/libc/src/__support/weak_avl.h b/libc/src/__support/weak_avl.h
index 2b2ad237c81b0..8504c77e8f2b9 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -80,7 +80,7 @@ template <typename T> class WeakAVLNode {
unsigned char left_rank_diff_2 : 1;
unsigned char right_rank_diff_2 : 1;
- LIBC_INLINE bool is_leaf() {
+ LIBC_INLINE bool is_leaf() const {
return (children[0] == nullptr) && (children[1] == nullptr);
}
@@ -209,6 +209,7 @@ template <typename T> class WeakAVLNode {
public:
LIBC_INLINE const WeakAVLNode *get_left() const { return children[0]; }
LIBC_INLINE const WeakAVLNode *get_right() const { return children[1]; }
+ LIBC_INLINE const WeakAVLNode *get_parent() const { return parent; }
LIBC_INLINE const T &get_data() const { return data; }
LIBC_INLINE bool has_rank_diff_2(bool is_right) const {
return is_right ? right_rank_diff_2 : left_rank_diff_2;
@@ -222,6 +223,17 @@ template <typename T> class WeakAVLNode {
destroy(node->children[1]);
::delete node;
}
+
+ // Destroy the subtree rooted at node with finalizer
+ template <typename Finalizer>
+ LIBC_INLINE static void destroy(WeakAVLNode *node, Finalizer finalizer) {
+ if (!node)
+ return;
+ destroy(node->children[0], finalizer);
+ destroy(node->children[1], finalizer);
+ finalizer(node->data);
+ ::delete node;
+ }
// Rotate the subtree rooted at node in the given direction.
//
// Illustration for is_right = true (Left Rotation):
@@ -555,25 +567,25 @@ template <typename T> class WeakAVLNode {
Leaf,
};
template <typename Func>
- LIBC_INLINE static void walk(WeakAVLNode *node, Func func) {
+ LIBC_INLINE static void walk(const WeakAVLNode *node, Func func,
+ int depth = 0) {
if (!node)
return;
if (node->is_leaf()) {
- func(node, WalkType::Leaf);
+ func(node, WalkType::Leaf, depth);
return;
}
- func(node, WalkType::PreOrder);
-
+ func(node, WalkType::PreOrder, depth);
if (node->children[0])
- walk(node->children[0], func);
+ walk(node->children[0], func, depth + 1);
- func(node, WalkType::InOrder);
+ func(node, WalkType::InOrder, depth);
if (node->children[1])
- walk(node->children[1], func);
- func(node, WalkType::PostOrder);
+ walk(node->children[1], func, depth + 1);
+ func(node, WalkType::PostOrder, depth);
}
};
diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt
index fdb79d1be27a5..2cdab57396bd1 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -133,7 +133,7 @@ add_entrypoint_object(
HDRS
tdelete.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
libc.src.__support.weak_avl
)
@@ -144,7 +144,7 @@ add_entrypoint_object(
HDRS
tfind.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
libc.src.__support.weak_avl
)
@@ -155,7 +155,7 @@ add_entrypoint_object(
HDRS
tsearch.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
libc.src.__support.weak_avl
)
@@ -166,7 +166,8 @@ add_entrypoint_object(
HDRS
twalk.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
+ libc.hdr.types.VISIT
libc.src.__support.weak_avl
)
@@ -177,7 +178,8 @@ add_entrypoint_object(
HDRS
twalk_r.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
+ libc.hdr.types.VISIT
libc.src.__support.weak_avl
)
@@ -188,6 +190,6 @@ add_entrypoint_object(
HDRS
tdestroy.h
DEPENDS
- libc.include.search
+ libc.hdr.types.posix_tnode
libc.src.__support.weak_avl
)
diff --git a/libc/src/search/tdelete.cpp b/libc/src/search/tdelete.cpp
index 3a1dbbb0e89aa..60c4a330753c9 100644
--- a/libc/src/search/tdelete.cpp
+++ b/libc/src/search/tdelete.cpp
@@ -13,13 +13,24 @@
namespace LIBC_NAMESPACE_DECL {
+// The tdelete() function shall return a pointer to the parent of the deleted
+// node, or an unspecified non-null pointer if the deleted node was the root
+// node, or a null pointer if the node is not found.
LLVM_LIBC_FUNCTION(void *, tdelete,
(const void *key, posix_tnode **rootp,
int (*compar)(const void *, const void *))) {
- (void)key;
- (void)rootp;
- (void)compar;
- return nullptr;
+ if (!rootp)
+ return nullptr;
+ using Node = WeakAVLNode<const void *>;
+ Node *&root = *reinterpret_cast<Node **>(rootp);
+ Node *node = Node::find(root, key, compar);
+ if (!node)
+ return nullptr;
+ void *result = const_cast<Node *>(node->get_parent());
+ if (!result)
+ result = cpp::bit_cast<void *>(uintptr_t(-1));
+ Node::erase(root, node);
+ return result;
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tdelete.h b/libc/src/search/tdelete.h
index 7e4f4a315cb27..168860304dbca 100644
--- a/libc/src/search/tdelete.h
+++ b/libc/src/search/tdelete.h
@@ -9,11 +9,11 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TDELETE_H
#define LLVM_LIBC_SRC_SEARCH_TDELETE_H
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-void *tdelete(const void *key, posix_tnode **rootp,
+void *tdelete(const void *key, __llvm_libc_tnode **rootp,
int (*compar)(const void *, const void *));
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tdestroy.cpp b/libc/src/search/tdestroy.cpp
index 024069d4cb951..3b93313879ae0 100644
--- a/libc/src/search/tdestroy.cpp
+++ b/libc/src/search/tdestroy.cpp
@@ -14,9 +14,15 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(void, tdestroy,
- (posix_tnode * root, void (*free_node)(void *))) {
- (void)root;
- (void)free_node;
+ (__llvm_libc_tnode * root, void (*free_node)(void *))) {
+ if (!root)
+ return;
+ using Node = WeakAVLNode<const void *>;
+ Node *node = reinterpret_cast<Node *>(root);
+ Node::destroy(node, [free_node](const void *&data) {
+ if (free_node)
+ free_node(const_cast<void *>(data));
+ });
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tdestroy.h b/libc/src/search/tdestroy.h
index 6290e1da634f3..fd2305b815079 100644
--- a/libc/src/search/tdestroy.h
+++ b/libc/src/search/tdestroy.h
@@ -9,11 +9,11 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TDESTROY_H
#define LLVM_LIBC_SRC_SEARCH_TDESTROY_H
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-void tdestroy(posix_tnode *root, void (*free_node)(void *));
+void tdestroy(__llvm_libc_tnode *root, void (*free_node)(void *));
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEARCH_TDESTROY_H
diff --git a/libc/src/search/tfind.cpp b/libc/src/search/tfind.cpp
index dd1da93f8478c..638076c67289d 100644
--- a/libc/src/search/tfind.cpp
+++ b/libc/src/search/tfind.cpp
@@ -13,13 +13,15 @@
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(posix_tnode *, tfind,
- (const void *key, posix_tnode *const *rootp,
+LLVM_LIBC_FUNCTION(__llvm_libc_tnode *, tfind,
+ (const void *key, __llvm_libc_tnode *const *rootp,
int (*compar)(const void *, const void *))) {
- (void)key;
- (void)rootp;
- (void)compar;
- return nullptr;
+ if (!rootp)
+ return nullptr;
+ using Node = WeakAVLNode<const void *>;
+ Node *root = reinterpret_cast<Node *>(*rootp);
+ Node *node = Node::find(root, key, compar);
+ return reinterpret_cast<__llvm_libc_tnode *>(node);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tfind.h b/libc/src/search/tfind.h
index aea252bf4195b..f44ec83c701dc 100644
--- a/libc/src/search/tfind.h
+++ b/libc/src/search/tfind.h
@@ -9,12 +9,12 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TFIND_H
#define LLVM_LIBC_SRC_SEARCH_TFIND_H
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-posix_tnode *tfind(const void *key, posix_tnode *const *rootp,
- int (*compar)(const void *, const void *));
+__llvm_libc_tnode *tfind(const void *key, __llvm_libc_tnode *const *rootp,
+ int (*compar)(const void *, const void *));
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEARCH_TFIND_H
diff --git a/libc/src/search/tsearch.cpp b/libc/src/search/tsearch.cpp
index 5b974a29bf8d5..32688915e84ef 100644
--- a/libc/src/search/tsearch.cpp
+++ b/libc/src/search/tsearch.cpp
@@ -13,13 +13,15 @@
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(posix_tnode *, tsearch,
- (const void *key, posix_tnode **rootp,
+LLVM_LIBC_FUNCTION(__llvm_libc_tnode *, tsearch,
+ (const void *key, __llvm_libc_tnode **rootp,
int (*compar)(const void *, const void *))) {
- (void)key;
- (void)rootp;
- (void)compar;
- return nullptr;
+ if (!rootp)
+ return nullptr;
+ using Node = WeakAVLNode<const void *>;
+ Node *&root = *reinterpret_cast<Node **>(rootp);
+ Node *node = Node::find_or_insert(root, key, compar);
+ return reinterpret_cast<__llvm_libc_tnode *>(node);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/tsearch.h b/libc/src/search/tsearch.h
index 565d852b019db..859c39832c1ec 100644
--- a/libc/src/search/tsearch.h
+++ b/libc/src/search/tsearch.h
@@ -9,12 +9,12 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TSEARCH_H
#define LLVM_LIBC_SRC_SEARCH_TSEARCH_H
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-posix_tnode *tsearch(const void *key, posix_tnode **rootp,
- int (*compar)(const void *, const void *));
+__llvm_libc_tnode *tsearch(const void *key, __llvm_libc_tnode **rootp,
+ int (*compar)(const void *, const void *));
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEARCH_TSEARCH_H
diff --git a/libc/src/search/twalk.cpp b/libc/src/search/twalk.cpp
index 5b655e8beaeaa..4b7ad98e3bc5a 100644
--- a/libc/src/search/twalk.cpp
+++ b/libc/src/search/twalk.cpp
@@ -7,17 +7,28 @@
//===----------------------------------------------------------------------===//
#include "src/search/twalk.h"
+#include "src/__support/CPP/bit.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/weak_avl.h"
namespace LIBC_NAMESPACE_DECL {
+using Node = WeakAVLNode<const void *>;
+
LLVM_LIBC_FUNCTION(void, twalk,
- (const posix_tnode *root,
- void (*action)(const posix_tnode *, VISIT, int))) {
- (void)root;
- (void)action;
+ (const __llvm_libc_tnode *root,
+ void (*action)(const __llvm_libc_tnode *, VISIT, int))) {
+ if (!root || !action)
+ return;
+ const Node *node = reinterpret_cast<const Node *>(root);
+ Node::walk(node, [action](const Node *n, Node::WalkType type, int depth) {
+ VISIT v = (type == Node::WalkType::PreOrder) ? preorder
+ : (type == Node::WalkType::InOrder) ? postorder
+ : (type == Node::WalkType::PostOrder) ? endorder
+ : leaf;
+ action(reinterpret_cast<const __llvm_libc_tnode *>(n), v, depth);
+ });
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/twalk.h b/libc/src/search/twalk.h
index ff599bbfbfbed..2353b6bf36191 100644
--- a/libc/src/search/twalk.h
+++ b/libc/src/search/twalk.h
@@ -9,12 +9,13 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TWALK_H
#define LLVM_LIBC_SRC_SEARCH_TWALK_H
+#include "hdr/types/VISIT.h"
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-void twalk(const posix_tnode *root,
- void (*action)(const posix_tnode *, VISIT, int));
+void twalk(const __llvm_libc_tnode *root,
+ void (*action)(const __llvm_libc_tnode *, VISIT, int));
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEARCH_TWALK_H
diff --git a/libc/src/search/twalk_r.cpp b/libc/src/search/twalk_r.cpp
index 47a741ed7723b..0f88becc2617f 100644
--- a/libc/src/search/twalk_r.cpp
+++ b/libc/src/search/twalk_r.cpp
@@ -13,13 +13,22 @@
namespace LIBC_NAMESPACE_DECL {
+using Node = WeakAVLNode<const void *>;
+
LLVM_LIBC_FUNCTION(void, twalk_r,
- (const posix_tnode *root,
- void (*action)(const posix_tnode *, VISIT, void *),
+ (const __llvm_libc_tnode *root,
+ void (*action)(const __llvm_libc_tnode *, VISIT, void *),
void *closure)) {
- (void)root;
- (void)action;
- (void)closure;
+ if (!root || !action)
+ return;
+ const Node *node = reinterpret_cast<const Node *>(root);
+ Node::walk(node, [action, closure](const Node *n, Node::WalkType type, int) {
+ VISIT v = (type == Node::WalkType::PreOrder) ? preorder
+ : (type == Node::WalkType::InOrder) ? postorder
+ : (type == Node::WalkType::PostOrder) ? endorder
+ : leaf;
+ action(reinterpret_cast<const __llvm_libc_tnode *>(n), v, closure);
+ });
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/twalk_r.h b/libc/src/search/twalk_r.h
index 6812bcea4995f..95a29256d8256 100644
--- a/libc/src/search/twalk_r.h
+++ b/libc/src/search/twalk_r.h
@@ -9,12 +9,14 @@
#ifndef LLVM_LIBC_SRC_SEARCH_TWALK_R_H
#define LLVM_LIBC_SRC_SEARCH_TWALK_R_H
+#include "hdr/types/VISIT.h"
+#include "hdr/types/posix_tnode.h"
#include "src/__support/macros/config.h"
-#include <search.h> // posix_tnode, VISIT
namespace LIBC_NAMESPACE_DECL {
-void twalk_r(const posix_tnode *root,
- void (*action)(const posix_tnode *, VISIT, void *), void *closure);
+void twalk_r(const __llvm_libc_tnode *root,
+ void (*action)(const __llvm_libc_tnode *, VISIT, void *),
+ void *closure);
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_SEARCH_TWALK_R_H
diff --git a/libc/test/src/__support/weak_avl_test.cpp b/libc/test/src/__support/weak_avl_test.cpp
index 65e26408a3707..b8291b6c78b15 100644
--- a/libc/test/src/__support/weak_avl_test.cpp
+++ b/libc/test/src/__support/weak_avl_test.cpp
@@ -278,11 +278,12 @@ TEST(LlvmLibcTreeWalk, InOrderTraversal) {
build_tree([](int x) { return stride(x, 1007); }, TEST_SIZE, compare);
int data[TEST_SIZE];
int counter = 0;
- WeakAVLNode::walk(root, [&](WeakAVLNode *node, WeakAVLNode::WalkType type) {
- if (type == WeakAVLNode::WalkType::InOrder ||
- type == WeakAVLNode::WalkType::Leaf)
- data[counter++] = node->get_data();
- });
+ WeakAVLNode::walk(
+ root, [&](const WeakAVLNode *node, WeakAVLNode::WalkType type, int) {
+ if (type == WeakAVLNode::WalkType::InOrder ||
+ type == WeakAVLNode::WalkType::Leaf)
+ data[counter++] = node->get_data();
+ });
for (int i = 0; i < TEST_SIZE; ++i)
ASSERT_EQ(data[i], i);
More information about the libc-commits
mailing list