[libc-commits] [libc] 0206846 - [libc] Implement alphasort in dirent (#226487)
via libc-commits
libc-commits at lists.llvm.org
Wed Sep 30 08:48:15 PDT 2026
Author: Aman Maurya
Date: 2026-09-30T08:48:07-07:00
New Revision: 0206846e12b5d9126758112aa9e410fe0c69c6b6
URL: https://github.com/llvm/llvm-project/commit/0206846e12b5d9126758112aa9e410fe0c69c6b6
DIFF: https://github.com/llvm/llvm-project/commit/0206846e12b5d9126758112aa9e410fe0c69c6b6.diff
LOG: [libc] Implement alphasort in dirent (#226487)
Implement the standard POSIX.1-2008 / POSIX.1-2024 function `alphasort`
in `<dirent.h>`, bringing functions declared in `dirent.yaml` to 100%
completion.
Fixes #226457
Added:
libc/src/dirent/alphasort.cpp
libc/src/dirent/alphasort.h
libc/test/src/dirent/alphasort_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/arm/entrypoints.txt
libc/config/linux/riscv/entrypoints.txt
libc/config/linux/x86_64/entrypoints.txt
libc/src/dirent/CMakeLists.txt
libc/src/string/CMakeLists.txt
libc/src/string/strcoll.cpp
libc/src/string/strcoll_l.cpp
libc/src/string/string_utils.h
libc/test/src/dirent/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index e1c4fbd656039..9bb9407a7624b 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1122,6 +1122,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.compiler.__stack_chk_fail
# dirent.h entrypoints
+ libc.src.dirent.alphasort
libc.src.dirent.closedir
libc.src.dirent.dirfd
libc.src.dirent.opendir
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 5f6f4ee9d558a..716ce89d2e6c5 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -328,6 +328,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.setjmp.sigsetjmp
# dirent.h entrypoints
+ libc.src.dirent.alphasort
libc.src.dirent.closedir
libc.src.dirent.dirfd
libc.src.dirent.opendir
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 069ca09e94eeb..475c6bbd5fbac 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1326,6 +1326,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.compiler.__stack_chk_fail
# dirent.h entrypoints
+ libc.src.dirent.alphasort
libc.src.dirent.closedir
libc.src.dirent.dirfd
libc.src.dirent.opendir
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 8257f2649f42d..1007ba4775cd7 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1348,6 +1348,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.compiler.__stack_chk_fail
# dirent.h entrypoints
+ libc.src.dirent.alphasort
libc.src.dirent.closedir
libc.src.dirent.dirfd
libc.src.dirent.opendir
diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index d0ec22e1cc75b..4061a04272de1 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -62,3 +62,18 @@ add_entrypoint_object(
libc.src.__support.File.dir
libc.src.errno.errno
)
+
+add_entrypoint_object(
+ alphasort
+ SRCS
+ alphasort.cpp
+ HDRS
+ alphasort.h
+ DEPENDS
+ libc.hdr.types.struct_dirent
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.string.string_utils
+)
+
diff --git a/libc/src/dirent/alphasort.cpp b/libc/src/dirent/alphasort.cpp
new file mode 100644
index 0000000000000..8a23ea9b51dce
--- /dev/null
+++ b/libc/src/dirent/alphasort.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of the POSIX alphasort function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/dirent/alphasort.h"
+
+#include "hdr/types/struct_dirent.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/null_check.h"
+#include "src/string/string_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, alphasort,
+ (const struct dirent **a, const struct dirent **b)) {
+ LIBC_CRASH_ON_NULLPTR(a);
+ LIBC_CRASH_ON_NULLPTR(b);
+ return internal::strcoll((*a)->d_name, (*b)->d_name);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/dirent/alphasort.h b/libc/src/dirent/alphasort.h
new file mode 100644
index 0000000000000..2a870a4cf414f
--- /dev/null
+++ b/libc/src/dirent/alphasort.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of the POSIX alphasort function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_DIRENT_ALPHASORT_H
+#define LLVM_LIBC_SRC_DIRENT_ALPHASORT_H
+
+#include "hdr/types/struct_dirent.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int alphasort(const struct dirent **a, const struct dirent **b);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_DIRENT_ALPHASORT_H
diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 7c132c9360d0b..0967ae5f83451 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -151,6 +151,11 @@ add_entrypoint_object(
strcoll.cpp
HDRS
strcoll.h
+ DEPENDS
+ .string_utils
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
)
add_entrypoint_object(
@@ -159,6 +164,11 @@ add_entrypoint_object(
strcoll_l.cpp
HDRS
strcoll_l.h
+ DEPENDS
+ .string_utils
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
)
add_entrypoint_object(
diff --git a/libc/src/string/strcoll.cpp b/libc/src/string/strcoll.cpp
index 7aa7cc2801ad8..88ed3aa0fd61b 100644
--- a/libc/src/string/strcoll.cpp
+++ b/libc/src/string/strcoll.cpp
@@ -11,6 +11,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/string/string_utils.h"
namespace LIBC_NAMESPACE_DECL {
@@ -18,9 +19,7 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, strcoll, (const char *left, const char *right)) {
LIBC_CRASH_ON_NULLPTR(left);
LIBC_CRASH_ON_NULLPTR(right);
- for (; *left && *left == *right; ++left, ++right)
- ;
- return static_cast<unsigned char>(*left) - static_cast<unsigned char>(*right);
+ return internal::strcoll(left, right);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/string/strcoll_l.cpp b/libc/src/string/strcoll_l.cpp
index e820efa564a3d..2390425b5db6d 100644
--- a/libc/src/string/strcoll_l.cpp
+++ b/libc/src/string/strcoll_l.cpp
@@ -11,6 +11,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/string/string_utils.h"
namespace LIBC_NAMESPACE_DECL {
@@ -19,9 +20,7 @@ LLVM_LIBC_FUNCTION(int, strcoll_l,
(const char *left, const char *right, locale_t)) {
LIBC_CRASH_ON_NULLPTR(left);
LIBC_CRASH_ON_NULLPTR(right);
- for (; *left && *left == *right; ++left, ++right)
- ;
- return static_cast<int>(*left) - static_cast<int>(*right);
+ return internal::strcoll(left, right);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h
index 8c8879b4886e8..5e9e9ec20f77c 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -25,6 +25,12 @@
namespace LIBC_NAMESPACE_DECL {
namespace internal {
+LIBC_INLINE int strcoll(const char *left, const char *right) {
+ for (; *left && *left == *right; ++left, ++right)
+ ;
+ return static_cast<unsigned char>(*left) - static_cast<unsigned char>(*right);
+}
+
// Returns the maximum length span that contains only characters not found in
// 'segment'. If no characters are found, returns the length of 'src'.
LIBC_INLINE size_t complementary_span(const char *src, const char *segment) {
diff --git a/libc/test/src/dirent/CMakeLists.txt b/libc/test/src/dirent/CMakeLists.txt
index 1d7a3caf790dd..dcd5985031abf 100644
--- a/libc/test/src/dirent/CMakeLists.txt
+++ b/libc/test/src/dirent/CMakeLists.txt
@@ -37,3 +37,16 @@ add_libc_test(
libc.hdr.types.DIR
libc.hdr.types.struct_dirent
)
+
+add_libc_test(
+ alphasort_test
+ SUITE
+ libc_dirent_unittests
+ SRCS
+ alphasort_test.cpp
+ DEPENDS
+ libc.hdr.types.struct_dirent
+ libc.src.dirent.alphasort
+ libc.src.stdlib.qsort
+)
+
diff --git a/libc/test/src/dirent/alphasort_test.cpp b/libc/test/src/dirent/alphasort_test.cpp
new file mode 100644
index 0000000000000..116a5a7c123e9
--- /dev/null
+++ b/libc/test/src/dirent/alphasort_test.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for the POSIX alphasort function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/dirent/alphasort.h"
+
+#include "hdr/types/struct_dirent.h"
+#include "src/stdlib/qsort.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+#include "src/string/string_length.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+struct MockDirent {
+ static constexpr size_t EXTRA_NAME_LEN = 64;
+ static constexpr size_t BUFFER_SIZE = sizeof(struct dirent) + EXTRA_NAME_LEN;
+
+ alignas(struct dirent) char buf[BUFFER_SIZE]{};
+
+ MockDirent(const char *name) {
+ auto *d = reinterpret_cast<struct dirent *>(buf);
+ size_t len = LIBC_NAMESPACE::internal::string_length(name);
+ EXPECT_LE(len, EXTRA_NAME_LEN);
+ if (len > EXTRA_NAME_LEN)
+ return;
+ LIBC_NAMESPACE::inline_memcpy(d->d_name, name, len);
+ d->d_name[len] = '\0';
+ }
+
+ const struct dirent *get() const {
+ return reinterpret_cast<const struct dirent *>(buf);
+ }
+};
+
+} // namespace
+
+TEST(LlvmLibcAlphasortTest, BasicComparison) {
+ MockDirent ent_a("apple");
+ MockDirent ent_b("banana");
+ const struct dirent *a = ent_a.get();
+ const struct dirent *b = ent_b.get();
+
+ EXPECT_LT(LIBC_NAMESPACE::alphasort(&a, &b), 0);
+ EXPECT_GT(LIBC_NAMESPACE::alphasort(&b, &a), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, EqualNames) {
+ MockDirent ent_1("file.txt");
+ MockDirent ent_2("file.txt");
+ const struct dirent *d1 = ent_1.get();
+ const struct dirent *d2 = ent_2.get();
+
+ EXPECT_EQ(LIBC_NAMESPACE::alphasort(&d1, &d2), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::alphasort(&d1, &d1), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, PrefixComparison) {
+ MockDirent ent_short("file");
+ MockDirent ent_long("file.txt");
+ const struct dirent *d_short = ent_short.get();
+ const struct dirent *d_long = ent_long.get();
+
+ EXPECT_LT(LIBC_NAMESPACE::alphasort(&d_short, &d_long), 0);
+ EXPECT_GT(LIBC_NAMESPACE::alphasort(&d_long, &d_short), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, EmptyName) {
+ MockDirent ent_empty("");
+ MockDirent ent_nonempty("a");
+ const struct dirent *d_empty = ent_empty.get();
+ const struct dirent *d_nonempty = ent_nonempty.get();
+
+ EXPECT_LT(LIBC_NAMESPACE::alphasort(&d_empty, &d_nonempty), 0);
+ EXPECT_GT(LIBC_NAMESPACE::alphasort(&d_nonempty, &d_empty), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::alphasort(&d_empty, &d_empty), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, QsortSorting) {
+ MockDirent ent_delta("delta");
+ MockDirent ent_alpha("alpha");
+ MockDirent ent_charlie("charlie");
+ MockDirent ent_bravo("bravo");
+
+ constexpr size_t NUM_ENTRIES = 4;
+ const struct dirent *entries[NUM_ENTRIES] = {
+ ent_delta.get(),
+ ent_alpha.get(),
+ ent_charlie.get(),
+ ent_bravo.get(),
+ };
+
+ using QsortComparator = int (*)(const void *, const void *);
+ LIBC_NAMESPACE::qsort(
+ entries, NUM_ENTRIES, sizeof(const struct dirent *),
+ reinterpret_cast<QsortComparator>(LIBC_NAMESPACE::alphasort));
+
+ EXPECT_STREQ(entries[0]->d_name, "alpha");
+ EXPECT_STREQ(entries[1]->d_name, "bravo");
+ EXPECT_STREQ(entries[2]->d_name, "charlie");
+ EXPECT_STREQ(entries[3]->d_name, "delta");
+}
More information about the libc-commits
mailing list