[libc-commits] [libc] [libc][tsearch] add tsearch functions (PR #172625)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Thu Jan 22 06:08:36 PST 2026


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/172625

>From 6622a513669065c4897cfb9d862539b184bffec2 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 17 Dec 2025 22:13:25 -0500
Subject: [PATCH] [libc][tsearch] add tsearch functions

---
 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 +++
 libc/hdr/types/posix_tnode.h                  |  28 ++++
 libc/include/CMakeLists.txt                   |   4 +
 libc/include/llvm-libc-types/CMakeLists.txt   |  16 ++
 .../llvm-libc-types/__action_closure_fn_t.h   |  17 +++
 libc/include/llvm-libc-types/__action_fn_t.h  |  17 +++
 libc/include/llvm-libc-types/__free_fn_t.h    |  14 ++
 libc/include/llvm-libc-types/posix_tnode.h    |  17 +++
 libc/include/search.yaml                      |  48 ++++++
 libc/src/__support/weak_avl.h                 |  30 ++--
 libc/src/search/CMakeLists.txt                |  68 +++++++++
 libc/src/search/tdelete.cpp                   |  36 +++++
 libc/src/search/tdelete.h                     |  20 +++
 libc/src/search/tdestroy.cpp                  |  28 ++++
 libc/src/search/tdestroy.h                    |  19 +++
 libc/src/search/tfind.cpp                     |  27 ++++
 libc/src/search/tfind.h                       |  20 +++
 libc/src/search/tsearch.cpp                   |  27 ++++
 libc/src/search/tsearch.h                     |  20 +++
 libc/src/search/twalk.cpp                     |  34 +++++
 libc/src/search/twalk.h                       |  21 +++
 libc/src/search/twalk_r.cpp                   |  34 +++++
 libc/src/search/twalk_r.h                     |  22 +++
 libc/test/src/search/CMakeLists.txt           |  15 ++
 libc/test/src/search/tsearch_test.cpp         | 137 ++++++++++++++++++
 28 files changed, 762 insertions(+), 9 deletions(-)
 create mode 100644 libc/hdr/types/VISIT.h
 create mode 100644 libc/hdr/types/posix_tnode.h
 create mode 100644 libc/include/llvm-libc-types/__action_closure_fn_t.h
 create mode 100644 libc/include/llvm-libc-types/__action_fn_t.h
 create mode 100644 libc/include/llvm-libc-types/__free_fn_t.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
 create mode 100644 libc/test/src/search/tsearch_test.cpp

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 362feefa67783..8301635c2d500 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -823,3 +823,21 @@ add_proxy_header_library(
   FULL_BUILD_DEPENDS
     libc.include.llvm-libc-types.Elf64_Versym
 )
+
+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 1bc9c2ca1a51d..f67f338764114 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 3124fdc33fbc2..9d32bac79337f 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)
@@ -550,3 +551,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/weak_avl.h b/libc/src/__support/weak_avl.h
index 31c7e31a19c6e..40222f44a38e2 100644
--- a/libc/src/__support/weak_avl.h
+++ b/libc/src/__support/weak_avl.h
@@ -82,7 +82,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);
   }
 
@@ -214,6 +214,7 @@ 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 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;
@@ -227,6 +228,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):
@@ -568,25 +580,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 0ed513e648ed1..2cdab57396bd1 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -125,3 +125,71 @@ 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.hdr.types.posix_tnode
+    libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+  tfind
+  SRCS
+    tfind.cpp
+  HDRS
+    tfind.h
+  DEPENDS
+    libc.hdr.types.posix_tnode
+    libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+  tsearch
+  SRCS
+    tsearch.cpp
+  HDRS
+    tsearch.h
+  DEPENDS
+    libc.hdr.types.posix_tnode
+    libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+  twalk
+  SRCS
+    twalk.cpp
+  HDRS
+    twalk.h
+  DEPENDS
+    libc.hdr.types.posix_tnode
+    libc.hdr.types.VISIT
+    libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+  twalk_r
+  SRCS
+    twalk_r.cpp
+  HDRS
+    twalk_r.h
+  DEPENDS
+    libc.hdr.types.posix_tnode
+    libc.hdr.types.VISIT
+    libc.src.__support.weak_avl
+)
+
+add_entrypoint_object(
+  tdestroy
+  SRCS
+    tdestroy.cpp
+  HDRS
+    tdestroy.h
+  DEPENDS
+    libc.hdr.types.posix_tnode
+    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..3fd5fc76bead4
--- /dev/null
+++ b/libc/src/search/tdelete.cpp
@@ -0,0 +1,36 @@
+//===-- 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 {
+
+// 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 *))) {
+  if (!rootp)
+    return nullptr;
+  using Node = WeakAVLNode<const void *>;
+  Node *&root = *reinterpret_cast<Node **>(rootp);
+  Node::OptionalNodePtr node = Node::find(root, key, compar);
+  if (!node)
+    return nullptr;
+  void *result = const_cast<Node *>(node.value()->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
new file mode 100644
index 0000000000000..168860304dbca
--- /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 "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+void *tdelete(const void *key, __llvm_libc_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..3b93313879ae0
--- /dev/null
+++ b/libc/src/search/tdestroy.cpp
@@ -0,0 +1,28 @@
+//===-- 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,
+                   (__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
new file mode 100644
index 0000000000000..fd2305b815079
--- /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 "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+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
new file mode 100644
index 0000000000000..640b8fe853d12
--- /dev/null
+++ b/libc/src/search/tfind.cpp
@@ -0,0 +1,27 @@
+//===-- 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(__llvm_libc_tnode *, tfind,
+                   (const void *key, __llvm_libc_tnode *const *rootp,
+                    int (*compar)(const void *, const void *))) {
+  if (!rootp)
+    return nullptr;
+  using Node = WeakAVLNode<const void *>;
+  Node *root = reinterpret_cast<Node *>(*rootp);
+  Node::OptionalNodePtr node = Node::find(root, key, compar);
+  return node ? reinterpret_cast<__llvm_libc_tnode *>(*node) : 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..f44ec83c701dc
--- /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 "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+__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
new file mode 100644
index 0000000000000..9363f15a6f284
--- /dev/null
+++ b/libc/src/search/tsearch.cpp
@@ -0,0 +1,27 @@
+//===-- 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(__llvm_libc_tnode *, tsearch,
+                   (const void *key, __llvm_libc_tnode **rootp,
+                    int (*compar)(const void *, const void *))) {
+  if (!rootp)
+    return nullptr;
+  using Node = WeakAVLNode<const void *>;
+  Node *&root = *reinterpret_cast<Node **>(rootp);
+  Node::OptionalNodePtr node = Node::find_or_insert(root, key, compar);
+  return node ? reinterpret_cast<__llvm_libc_tnode *>(*node) : 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..859c39832c1ec
--- /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 "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+__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
new file mode 100644
index 0000000000000..4b7ad98e3bc5a
--- /dev/null
+++ b/libc/src/search/twalk.cpp
@@ -0,0 +1,34 @@
+//===-- 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/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 __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
new file mode 100644
index 0000000000000..2353b6bf36191
--- /dev/null
+++ b/libc/src/search/twalk.h
@@ -0,0 +1,21 @@
+//===-- 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 "hdr/types/VISIT.h"
+#include "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+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
new file mode 100644
index 0000000000000..0f88becc2617f
--- /dev/null
+++ b/libc/src/search/twalk_r.cpp
@@ -0,0 +1,34 @@
+//===-- 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 {
+
+using Node = WeakAVLNode<const void *>;
+
+LLVM_LIBC_FUNCTION(void, twalk_r,
+                   (const __llvm_libc_tnode *root,
+                    void (*action)(const __llvm_libc_tnode *, VISIT, void *),
+                    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
new file mode 100644
index 0000000000000..95a29256d8256
--- /dev/null
+++ b/libc/src/search/twalk_r.h
@@ -0,0 +1,22 @@
+//===-- 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 "hdr/types/VISIT.h"
+#include "hdr/types/posix_tnode.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+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/search/CMakeLists.txt b/libc/test/src/search/CMakeLists.txt
index 9f34d4d3fd259..273ce9722148b 100644
--- a/libc/test/src/search/CMakeLists.txt
+++ b/libc/test/src/search/CMakeLists.txt
@@ -45,3 +45,18 @@ add_libc_unittest(
   DEPENDS
     libc.src.search.lsearch
 )
+
+add_libc_unittest(
+  tsearch_test
+  SUITE
+    libc_search_unittests
+  SRCS
+    tsearch_test.cpp
+  DEPENDS
+    libc.src.search.tsearch
+    libc.src.search.tfind
+    libc.src.search.tdelete
+    libc.src.search.twalk
+    libc.src.search.twalk_r
+    libc.src.search.tdestroy
+)
diff --git a/libc/test/src/search/tsearch_test.cpp b/libc/test/src/search/tsearch_test.cpp
new file mode 100644
index 0000000000000..609b6207f842b
--- /dev/null
+++ b/libc/test/src/search/tsearch_test.cpp
@@ -0,0 +1,137 @@
+//===-- Unittests for tsearch ---------------------------------------------===//
+//
+// 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 "hdr/types/posix_tnode.h"
+#include "src/search/tdelete.h"
+#include "src/search/tdestroy.h"
+#include "src/search/tfind.h"
+#include "src/search/tsearch.h"
+#include "src/search/twalk.h"
+#include "src/search/twalk_r.h"
+#include "test/UnitTest/Test.h"
+#include <search.h>
+
+static void *encode(int val) {
+  return reinterpret_cast<void *>(static_cast<intptr_t>(val));
+}
+
+static int decode(const void *val) {
+  return static_cast<int>(reinterpret_cast<intptr_t>(val));
+}
+
+static int read_and_decode(const __llvm_libc_tnode *val) {
+  return static_cast<int>(*static_cast<const intptr_t *>(val));
+}
+
+static int compare(const void *a, const void *b) {
+  int x = decode(a);
+  int y = decode(b);
+  return (x > y) - (x < y);
+}
+
+TEST(LlvmLibcTSearchTest, TSearch) {
+  void *root = nullptr;
+  void *result;
+  int key = 10;
+
+  // Insert 10
+  result = LIBC_NAMESPACE::tsearch(encode(key), &root, compare);
+  ASSERT_NE(result, nullptr);
+  ASSERT_EQ(read_and_decode(result), key);
+
+  // Find 10
+  result = LIBC_NAMESPACE::tfind(encode(key), &root, compare);
+  ASSERT_NE(result, nullptr);
+  ASSERT_EQ(read_and_decode(result), key);
+
+  // Insert 20
+  int key2 = 20;
+  result = LIBC_NAMESPACE::tsearch(encode(key2), &root, compare);
+  ASSERT_NE(result, nullptr);
+  ASSERT_EQ(read_and_decode(result), key2);
+
+  // Find 20
+  result = LIBC_NAMESPACE::tfind(encode(key2), &root, compare);
+  ASSERT_NE(result, nullptr);
+  ASSERT_EQ(read_and_decode(result), key2);
+
+  // Delete 10
+  result = LIBC_NAMESPACE::tdelete(encode(key), &root, compare);
+  ASSERT_NE(result, nullptr);
+
+  // Find 10 should fail
+  result = LIBC_NAMESPACE::tfind(encode(key), &root, compare);
+  ASSERT_EQ(result, nullptr);
+
+  // Delete 20
+  result = LIBC_NAMESPACE::tdelete(encode(key2), &root, compare);
+  ASSERT_NE(result, nullptr);
+  // Tree should be empty
+  ASSERT_EQ(root, nullptr);
+}
+
+static void free_node(void *) {
+  // Do nothing for integer keys
+}
+
+TEST(LlvmLibcTSearchTest, TDestroy) {
+  void *root = nullptr;
+  for (int i = 0; i < 10; ++i)
+    LIBC_NAMESPACE::tsearch(encode(i), &root, compare);
+
+  LIBC_NAMESPACE::tdestroy(root, free_node);
+}
+
+static int walk_sum = 0;
+static void action(const __llvm_libc_tnode *node, VISIT visit, int) {
+  if (visit == leaf || visit == postorder)
+    walk_sum += read_and_decode(node);
+}
+
+TEST(LlvmLibcTSearchTest, TWalk) {
+  void *root = nullptr;
+  int sum = 0;
+  for (int i = 1; i <= 5; ++i) {
+    LIBC_NAMESPACE::tsearch(encode(i), &root, compare);
+    sum += i;
+  }
+
+  walk_sum = 0;
+  LIBC_NAMESPACE::twalk(root, action);
+  ASSERT_EQ(walk_sum, sum);
+
+  LIBC_NAMESPACE::tdestroy(root, free_node);
+}
+
+static void action_closure(const __llvm_libc_tnode *node, VISIT visit,
+                           void *closure) {
+  if (visit == leaf || visit == postorder) {
+    int **cursor = static_cast<int **>(closure);
+    **cursor = read_and_decode(node);
+    *cursor += 1;
+  }
+}
+
+TEST(LlvmLibcTSearchTest, TWalkR) {
+  void *root = nullptr;
+  constexpr int LIMIT = 64;
+  int sorted[LIMIT];
+  for (int i = 0; i < LIMIT; ++i) {
+    int current = (i * 37) % LIMIT; // pseudo-random insertion order
+    LIBC_NAMESPACE::tsearch(encode(current), &root, compare);
+  }
+
+  walk_sum = 0;
+  int *cursor = &sorted[0];
+  LIBC_NAMESPACE::twalk_r(root, action_closure, &cursor);
+
+  for (int i = 0; i < LIMIT; ++i)
+    ASSERT_EQ(sorted[i], i);
+
+  LIBC_NAMESPACE::tdestroy(root, free_node);
+}



More information about the libc-commits mailing list