Skip to content

Conversation

@SchrodingerZhu
Copy link
Contributor

  • [libc][tsearch] scaffold WAVL header
  • stage
  • stage
  • stage the work
  • stage the work
  • update ref
  • update
  • update
  • update
  • update
  • add fuzzing
  • reduce size for hermetic test
  • improve doc
  • add tree walk API
  • reduce test size further for hermetic tests
  • add doc
  • separate flag and parent pointer
  • stage

@SchrodingerZhu SchrodingerZhu force-pushed the libc/wavl-tsearch branch 4 times, most recently from a9215c1 to 51042f7 Compare December 18, 2025 03:13
@SchrodingerZhu SchrodingerZhu marked this pull request as ready for review December 18, 2025 03:23
@llvmbot llvmbot added the libc label Dec 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 18, 2025

@llvm/pr-subscribers-libc

Author: Schrodinger ZHU Yifan (SchrodingerZhu)

Changes
  • [libc][tsearch] scaffold WAVL header
  • stage
  • stage
  • stage the work
  • stage the work
  • update ref
  • update
  • update
  • update
  • update
  • add fuzzing
  • reduce size for hermetic test
  • improve doc
  • add tree walk API
  • reduce test size further for hermetic tests
  • add doc
  • separate flag and parent pointer
  • stage

Patch is 66.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/172625.diff

33 Files Affected:

  • (modified) libc/config/linux/aarch64/entrypoints.txt (+6)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+6)
  • (modified) libc/fuzzing/__support/CMakeLists.txt (+8)
  • (added) libc/fuzzing/__support/weak_avl_fuzz.cpp (+94)
  • (modified) libc/hdr/types/CMakeLists.txt (+18)
  • (added) libc/hdr/types/VISIT.h (+22)
  • (added) libc/hdr/types/posix_tnode.h (+28)
  • (modified) libc/include/CMakeLists.txt (+4)
  • (modified) libc/include/llvm-libc-types/CMakeLists.txt (+16)
  • (added) libc/include/llvm-libc-types/__action_closure_fn_t.h (+17)
  • (added) libc/include/llvm-libc-types/__action_fn_t.h (+17)
  • (added) libc/include/llvm-libc-types/__free_fn_t.h (+14)
  • (added) libc/include/llvm-libc-types/posix_tnode.h (+17)
  • (modified) libc/include/search.yaml (+48)
  • (modified) libc/src/__support/CMakeLists.txt (+14)
  • (added) libc/src/__support/weak_avl.h (+595)
  • (modified) libc/src/search/CMakeLists.txt (+68)
  • (added) libc/src/search/tdelete.cpp (+36)
  • (added) libc/src/search/tdelete.h (+20)
  • (added) libc/src/search/tdestroy.cpp (+28)
  • (added) libc/src/search/tdestroy.h (+19)
  • (added) libc/src/search/tfind.cpp (+27)
  • (added) libc/src/search/tfind.h (+20)
  • (added) libc/src/search/tsearch.cpp (+27)
  • (added) libc/src/search/tsearch.h (+20)
  • (added) libc/src/search/twalk.cpp (+34)
  • (added) libc/src/search/twalk.h (+21)
  • (added) libc/src/search/twalk_r.cpp (+34)
  • (added) libc/src/search/twalk_r.h (+22)
  • (modified) libc/test/src/__support/CMakeLists.txt (+10)
  • (added) libc/test/src/__support/weak_avl_test.cpp (+291)
  • (modified) libc/test/src/search/CMakeLists.txt (+15)
  • (added) libc/test/src/search/tsearch_test.cpp (+137)
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/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
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/posix_tnode.h b/libc/hdr/types/posix_tnode.h
new file mode 100644
index 0000000000000..0b2ecfd488217
--- /dev/null
+++ b/libc/hdr/types/posix_tnode.h
@@ -0,0 +1,28 @@
+//===-- 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.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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.
+// internal code should use __llvm_libc_tnode.
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/posix_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_POSIX_TNODE_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 96c3a88f3fcc8..c72bb5a6eb280 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -273,9 +273,13 @@ 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
+    .llvm-libc-types.__action_closure_fn_t
+    .llvm-libc-types.__action_fn_t
+    .llvm-libc-types.__free_fn_t
     .llvm_libc_common_h
 )
 
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index fcd0e1245a4a8..0c6d4d8e870d3 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)
@@ -310,3 +311,18 @@ add_header(EFI_SYSTEM_TABLE
     .EFI_TABLE_HEADER
     .char16_t
 )
+add_header(__action_closure_fn_t 
+  HDR 
+    __action_closure_fn_t.h 
+  DEPENDS 
+    .posix_tnode 
+    .VISIT
+)
+add_header(__action_fn_t 
+  HDR 
+    __action_fn_t.h 
+  DEPENDS 
+    .posix_tnode 
+    .VISIT
+)
+add_header(__free_fn_t HDR __free_fn_t.h)
diff --git a/libc/include/llvm-libc-types/__action_closure_fn_t.h b/libc/include/llvm-libc-types/__action_closure_fn_t.h
new file mode 100644
index 0000000000000..18d91a3f53925
--- /dev/null
+++ b/libc/include/llvm-libc-types/__action_closure_fn_t.h
@@ -0,0 +1,17 @@
+//===-- Definition of type __action_closure_fn_t --------------------------===//
+//
+// 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___ACTION_CLOSURE_FN_T_H
+#define LLVM_LIBC_TYPES___ACTION_CLOSURE_FN_T_H
+
+#include "VISIT.h"
+#include "posix_tnode.h"
+
+typedef void (*__action_closure_fn_t)(const posix_tnode *, VISIT, void *);
+
+#endif // LLVM_LIBC_TYPES___ACTION_CLOSURE_FN_T_H
diff --git a/libc/include/llvm-libc-types/__action_fn_t.h b/libc/include/llvm-libc-types/__action_fn_t.h
new file mode 100644
index 0000000000000..de3dc02e4eacd
--- /dev/null
+++ b/libc/include/llvm-libc-types/__action_fn_t.h
@@ -0,0 +1,17 @@
+//===-- Definition of type __action_fn_t ----------------------------------===//
+//
+// 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___ACTION_FN_T_H
+#define LLVM_LIBC_TYPES___ACTION_FN_T_H
+
+#include "VISIT.h"
+#include "posix_tnode.h"
+
+typedef void (*__action_fn_t)(const posix_tnode *, VISIT, int);
+
+#endif // LLVM_LIBC_TYPES___ACTION_FN_T_H
diff --git a/libc/include/llvm-libc-types/__free_fn_t.h b/libc/include/llvm-libc-types/__free_fn_t.h
new file mode 100644
index 0000000000000..72f00f3df5796
--- /dev/null
+++ b/libc/include/llvm-libc-types/__free_fn_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of type __free_fn_t ------------------------------------===//
+//
+// 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___FREE_FN_T_H
+#define LLVM_LIBC_TYPES___FREE_FN_T_H
+
+typedef void (*__free_fn_t)(void *);
+
+#endif // LLVM_LIBC_TYPES___FREE_FN_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..50f317820e1b5 100644
--- a/libc/include/search.yaml
+++ b/libc/include/search.yaml
@@ -7,6 +7,10 @@ types:
   - type_name: VISIT
   - type_name: __search_compare_t
   - type_name: struct_hsearch_data
+  - type_name: posix_tnode
+  - type_name: __action_closure_fn_t
+  - type_name: __action_fn_t
+  - type_name: __free_fn_t
 enums: []
 objects: []
 functions:
@@ -80,3 +84,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: __action_fn_t
+  - name: twalk_r
+    standards: gnu
+    return_type: void
+    arguments:
+      - type: const posix_tnode *
+      - type: __action_closure_fn_t
+      - type: void *
+  - name: tdestroy
+    standards: gnu
+    return_type: void
+    arguments:
+      - type: posix_tnode *
+      - type: __free_fn_t
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index c7f127d6934a0..bde12abaa291e 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -391,6 +391,20 @@ add_header_library(
     libc.src.__support.macros.attributes
 )
 
+add_header_library(
+  weak_avl
+  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
+)
+
 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..6ad85ca4b9b8f
--- /dev/null
+++ b/libc/src/__support/weak_avl.h
@@ -0,0 +1,595 @@
+//===-- 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 "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/libc_assert.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+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;
+
+  // Parent pointer
+  WeakAVLNode *parent;
+
+  // Children pointers
+  WeakAVLNode *children[2];
+
+  // Flags
+  unsigned char left_rank_diff_2 : 1;
+  unsigned char right_rank_diff_2 : 1;
+
+  LIBC_INLINE bool is_leaf() const {
+    return (children[0] == nullptr) && (children[1] == nullptr);
+  }
+
+  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 both_flags_set() const {
+    return left_rank_diff_2 && right_rank_diff_2;
+  }
+
+  LIBC_INLINE bool any_flag_set() const {
+    return left_rank_diff_2 || right_rank_diff_2;
+  }
+
+  LIBC_INLINE void clear_flags() {
+    left_rank_diff_2 = 0;
+    right_rank_diff_2 = 0;
+  }
+
+  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(nullptr), children{nullptr, nullptr},
+        left_rank_diff_2(0), right_rank_diff_2(0) {}
+
+  LIBC_INLINE static WeakAVLNode *create(T value) {
+    AllocChecker ac;
+    WeakAVLNode *res = new (ac) WeakAVLNode(value);
+    if (ac)
+      return res;
+    return nullptr;
+  }
+
+  // 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 update all the pointers and flags.
+  struct FixupSite {
+    WeakAVLNode *parent;
+  ...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants