[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