[libc-commits] [libc] [libc] Implement alphasort in dirent (PR #226487)

Aman Maurya via libc-commits libc-commits at lists.llvm.org
Tue Sep 29 18:13:37 PDT 2026


https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/226487

>From 34abd348c27e1e722a026c3f297c1d973c9da129 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Fri, 25 Sep 2026 18:07:01 +0530
Subject: [PATCH 1/5] [libc] Implement alphasort in dirent

Implement the standard POSIX.1-2008 / POSIX.1-2024 function lphasort in <dirent.h>, bringing declared <dirent.h> POSIX functions to 100% completion.

Fixes #226457

Assisted by Gemini.
---
 libc/config/linux/aarch64/entrypoints.txt |   1 +
 libc/config/linux/arm/entrypoints.txt     |   1 +
 libc/config/linux/riscv/entrypoints.txt   |   1 +
 libc/config/linux/x86_64/entrypoints.txt  |   1 +
 libc/src/dirent/CMakeLists.txt            |  14 +++
 libc/src/dirent/alphasort.cpp             |  27 ++++++
 libc/src/dirent/alphasort.h               |  26 ++++++
 libc/test/src/dirent/CMakeLists.txt       |  13 +++
 libc/test/src/dirent/alphasort_test.cpp   | 105 ++++++++++++++++++++++
 9 files changed, 189 insertions(+)
 create mode 100644 libc/src/dirent/alphasort.cpp
 create mode 100644 libc/src/dirent/alphasort.h
 create mode 100644 libc/test/src/dirent/alphasort_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 15a5367f5fc95..614e17bf21a97 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1117,6 +1117,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 1441146393c5f..07b75376a6057 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -324,6 +324,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 8f4dded27de68..436f5198f6c4d 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1321,6 +1321,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 8ca73cc82cc2d..2ab0d8c06e291 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1343,6 +1343,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..3254141e2c96d 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -62,3 +62,17 @@ 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.string.strcoll
+)
+
diff --git a/libc/src/dirent/alphasort.cpp b/libc/src/dirent/alphasort.cpp
new file mode 100644
index 0000000000000..91d45c899e415
--- /dev/null
+++ b/libc/src/dirent/alphasort.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/string/strcoll.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, alphasort,
+                   (const struct dirent **a, const struct dirent **b)) {
+  return LIBC_NAMESPACE::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/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..045ec0e418794
--- /dev/null
+++ b/libc/test/src/dirent/alphasort_test.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "test/UnitTest/Test.h"
+
+namespace {
+
+template <size_t N = 64> struct MockDirent {
+  alignas(struct dirent) char buf[sizeof(struct dirent) + N]{};
+
+  const struct dirent *create(const char *name) {
+    auto *d = reinterpret_cast<struct dirent *>(buf);
+    char *dst = d->d_name;
+    while (*name)
+      *dst++ = *name++;
+    *dst = '\0';
+    return d;
+  }
+};
+
+} // namespace
+
+TEST(LlvmLibcAlphasortTest, BasicComparison) {
+  MockDirent<> ent_a{};
+  MockDirent<> ent_b{};
+  const struct dirent *a = ent_a.create("apple");
+  const struct dirent *b = ent_b.create("banana");
+
+  EXPECT_LT(LIBC_NAMESPACE::alphasort(&a, &b), 0);
+  EXPECT_GT(LIBC_NAMESPACE::alphasort(&b, &a), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, EqualNames) {
+  MockDirent<> ent_1{};
+  MockDirent<> ent_2{};
+  const struct dirent *d1 = ent_1.create("file.txt");
+  const struct dirent *d2 = ent_2.create("file.txt");
+
+  EXPECT_EQ(LIBC_NAMESPACE::alphasort(&d1, &d2), 0);
+  EXPECT_EQ(LIBC_NAMESPACE::alphasort(&d1, &d1), 0);
+}
+
+TEST(LlvmLibcAlphasortTest, PrefixComparison) {
+  MockDirent<> ent_short{};
+  MockDirent<> ent_long{};
+  const struct dirent *d_short = ent_short.create("file");
+  const struct dirent *d_long = ent_long.create("file.txt");
+
+  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{};
+  const struct dirent *d_empty = ent_empty.create("");
+  const struct dirent *d_nonempty = ent_nonempty.create("a");
+
+  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{};
+  MockDirent<> ent_alpha{};
+  MockDirent<> ent_charlie{};
+  MockDirent<> ent_bravo{};
+
+  constexpr size_t NUM_ENTRIES = 4;
+  const struct dirent *entries[NUM_ENTRIES] = {
+      ent_delta.create("delta"),
+      ent_alpha.create("alpha"),
+      ent_charlie.create("charlie"),
+      ent_bravo.create("bravo"),
+  };
+
+  using QsortComparator = int (*)(const void *, const void *);
+  LIBC_NAMESPACE::qsort(
+      entries, NUM_ENTRIES, sizeof(const struct dirent *),
+      reinterpret_cast<QsortComparator>(LIBC_NAMESPACE::alphasort));
+
+  const struct dirent *e0 = entries[0];
+  const struct dirent *e1 = entries[1];
+  const struct dirent *e2 = entries[2];
+  const struct dirent *e3 = entries[3];
+
+  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e0, &e1), 0); // alpha < bravo
+  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e1, &e2), 0); // bravo < charlie
+  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e2, &e3), 0); // charlie < delta
+}

>From 44bdb02ed4bcdd90241a3aeea2516f8a0dbec711 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Fri, 25 Sep 2026 22:22:59 +0530
Subject: [PATCH 2/5] [libc] Factor out strcoll into string_utils for alphasort

Avoid entrypoint-to-entrypoint dependency between alphasort and strcoll by moving the comparison logic into the shared internal header library string_utils (internal::strcoll).
---
 libc/src/dirent/CMakeLists.txt |  2 +-
 libc/src/dirent/alphasort.cpp  |  4 ++--
 libc/src/string/CMakeLists.txt |  9 +++++++++
 libc/src/string/strcoll.cpp    |  9 ++-------
 libc/src/string/strcoll_l.cpp  |  8 ++------
 libc/src/string/string_utils.h | 10 ++++++++++
 6 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index 3254141e2c96d..112bcca99c864 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -73,6 +73,6 @@ add_entrypoint_object(
     libc.hdr.types.struct_dirent
     libc.src.__support.common
     libc.src.__support.macros.config
-    libc.src.string.strcoll
+    libc.src.string.string_utils
 )
 
diff --git a/libc/src/dirent/alphasort.cpp b/libc/src/dirent/alphasort.cpp
index 91d45c899e415..0cf15cee47815 100644
--- a/libc/src/dirent/alphasort.cpp
+++ b/libc/src/dirent/alphasort.cpp
@@ -15,13 +15,13 @@
 
 #include "hdr/types/struct_dirent.h"
 #include "src/__support/common.h"
-#include "src/string/strcoll.h"
+#include "src/string/string_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, alphasort,
                    (const struct dirent **a, const struct dirent **b)) {
-  return LIBC_NAMESPACE::strcoll((*a)->d_name, (*b)->d_name);
+  return internal::strcoll((*a)->d_name, (*b)->d_name);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 7c132c9360d0b..87e0d8678fb1e 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -13,6 +13,7 @@ add_header_library(
     libc.src.__support.CPP.simd
     libc.src.__support.common
     libc.src.__support.macros.attributes
+    libc.src.__support.macros.null_check
     libc.src.string.memory_utils.inline_memcpy
 )
 
@@ -151,6 +152,10 @@ add_entrypoint_object(
     strcoll.cpp
   HDRS
     strcoll.h
+  DEPENDS
+    .string_utils
+    libc.src.__support.common
+    libc.src.__support.macros.config
 )
 
 add_entrypoint_object(
@@ -159,6 +164,10 @@ add_entrypoint_object(
     strcoll_l.cpp
   HDRS
     strcoll_l.h
+  DEPENDS
+    .string_utils
+    libc.src.__support.common
+    libc.src.__support.macros.config
 )
 
 add_entrypoint_object(
diff --git a/libc/src/string/strcoll.cpp b/libc/src/string/strcoll.cpp
index 7aa7cc2801ad8..4763fa112ffbb 100644
--- a/libc/src/string/strcoll.cpp
+++ b/libc/src/string/strcoll.cpp
@@ -10,17 +10,12 @@
 
 #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 {
 
-// TODO: Add support for locales.
 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..61480cba90507 100644
--- a/libc/src/string/strcoll_l.cpp
+++ b/libc/src/string/strcoll_l.cpp
@@ -10,18 +10,14 @@
 
 #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 {
 
 // TODO: Add support for locales.
 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..378dd26485a96 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -18,6 +18,7 @@
 #include "src/__support/CPP/bitset.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 #include "src/string/memory_utils/inline_memcpy.h"
 #include "src/string/string_length.h"
@@ -25,6 +26,15 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
+// TODO: Add support for locales.
+LIBC_INLINE 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);
+}
+
 // 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) {

>From e96ef587f030acd7701e9b24603d4b0466582b8c Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Tue, 29 Sep 2026 08:58:12 +0530
Subject: [PATCH 3/5] [libc] Address PR feedback for alphasort and strcoll

- Restore LIBC_CRASH_ON_NULLPTR in strcoll and strcoll_l entrypoints

- Add LIBC_CRASH_ON_NULLPTR in alphasort entrypoint

- Remove null checks and locale comment from internal::strcoll

- Update CMake dependencies for null_check

- Simplify MockDirent in alphasort_test to a regular struct
---
 libc/src/dirent/CMakeLists.txt          |  1 +
 libc/src/dirent/alphasort.cpp           |  3 ++
 libc/src/string/CMakeLists.txt          |  3 +-
 libc/src/string/strcoll.cpp             |  4 ++
 libc/src/string/strcoll_l.cpp           |  3 ++
 libc/src/string/string_utils.h          |  5 +--
 libc/test/src/dirent/alphasort_test.cpp | 59 +++++++++++++------------
 7 files changed, 45 insertions(+), 33 deletions(-)

diff --git a/libc/src/dirent/CMakeLists.txt b/libc/src/dirent/CMakeLists.txt
index 112bcca99c864..4061a04272de1 100644
--- a/libc/src/dirent/CMakeLists.txt
+++ b/libc/src/dirent/CMakeLists.txt
@@ -73,6 +73,7 @@ add_entrypoint_object(
     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
index 0cf15cee47815..8a23ea9b51dce 100644
--- a/libc/src/dirent/alphasort.cpp
+++ b/libc/src/dirent/alphasort.cpp
@@ -15,12 +15,15 @@
 
 #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);
 }
 
diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 87e0d8678fb1e..0967ae5f83451 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -13,7 +13,6 @@ add_header_library(
     libc.src.__support.CPP.simd
     libc.src.__support.common
     libc.src.__support.macros.attributes
-    libc.src.__support.macros.null_check
     libc.src.string.memory_utils.inline_memcpy
 )
 
@@ -156,6 +155,7 @@ add_entrypoint_object(
     .string_utils
     libc.src.__support.common
     libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
 )
 
 add_entrypoint_object(
@@ -168,6 +168,7 @@ add_entrypoint_object(
     .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 4763fa112ffbb..88ed3aa0fd61b 100644
--- a/libc/src/string/strcoll.cpp
+++ b/libc/src/string/strcoll.cpp
@@ -10,11 +10,15 @@
 
 #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 {
 
+// TODO: Add support for locales.
 LLVM_LIBC_FUNCTION(int, strcoll, (const char *left, const char *right)) {
+  LIBC_CRASH_ON_NULLPTR(left);
+  LIBC_CRASH_ON_NULLPTR(right);
   return internal::strcoll(left, right);
 }
 
diff --git a/libc/src/string/strcoll_l.cpp b/libc/src/string/strcoll_l.cpp
index 61480cba90507..2390425b5db6d 100644
--- a/libc/src/string/strcoll_l.cpp
+++ b/libc/src/string/strcoll_l.cpp
@@ -10,6 +10,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 {
@@ -17,6 +18,8 @@ namespace LIBC_NAMESPACE_DECL {
 // TODO: Add support for locales.
 LLVM_LIBC_FUNCTION(int, strcoll_l,
                    (const char *left, const char *right, locale_t)) {
+  LIBC_CRASH_ON_NULLPTR(left);
+  LIBC_CRASH_ON_NULLPTR(right);
   return internal::strcoll(left, right);
 }
 
diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h
index 378dd26485a96..e3f7335ca6d8a 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -18,7 +18,7 @@
 #include "src/__support/CPP/bitset.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
-#include "src/__support/macros/null_check.h"
+
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 #include "src/string/memory_utils/inline_memcpy.h"
 #include "src/string/string_length.h"
@@ -26,10 +26,7 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
-// TODO: Add support for locales.
 LIBC_INLINE 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);
diff --git a/libc/test/src/dirent/alphasort_test.cpp b/libc/test/src/dirent/alphasort_test.cpp
index 045ec0e418794..11ef804a47b65 100644
--- a/libc/test/src/dirent/alphasort_test.cpp
+++ b/libc/test/src/dirent/alphasort_test.cpp
@@ -19,56 +19,59 @@
 
 namespace {
 
-template <size_t N = 64> struct MockDirent {
-  alignas(struct dirent) char buf[sizeof(struct dirent) + N]{};
+struct MockDirent {
+  alignas(struct dirent) char buf[sizeof(struct dirent) + 64]{};
 
-  const struct dirent *create(const char *name) {
+  MockDirent(const char *name) {
     auto *d = reinterpret_cast<struct dirent *>(buf);
     char *dst = d->d_name;
     while (*name)
       *dst++ = *name++;
     *dst = '\0';
-    return d;
+  }
+
+  const struct dirent *get() const {
+    return reinterpret_cast<const struct dirent *>(buf);
   }
 };
 
 } // namespace
 
 TEST(LlvmLibcAlphasortTest, BasicComparison) {
-  MockDirent<> ent_a{};
-  MockDirent<> ent_b{};
-  const struct dirent *a = ent_a.create("apple");
-  const struct dirent *b = ent_b.create("banana");
+  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{};
-  MockDirent<> ent_2{};
-  const struct dirent *d1 = ent_1.create("file.txt");
-  const struct dirent *d2 = ent_2.create("file.txt");
+  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{};
-  MockDirent<> ent_long{};
-  const struct dirent *d_short = ent_short.create("file");
-  const struct dirent *d_long = ent_long.create("file.txt");
+  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{};
-  const struct dirent *d_empty = ent_empty.create("");
-  const struct dirent *d_nonempty = ent_nonempty.create("a");
+  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);
@@ -76,17 +79,17 @@ TEST(LlvmLibcAlphasortTest, EmptyName) {
 }
 
 TEST(LlvmLibcAlphasortTest, QsortSorting) {
-  MockDirent<> ent_delta{};
-  MockDirent<> ent_alpha{};
-  MockDirent<> ent_charlie{};
-  MockDirent<> ent_bravo{};
+  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.create("delta"),
-      ent_alpha.create("alpha"),
-      ent_charlie.create("charlie"),
-      ent_bravo.create("bravo"),
+      ent_delta.get(),
+      ent_alpha.get(),
+      ent_charlie.get(),
+      ent_bravo.get(),
   };
 
   using QsortComparator = int (*)(const void *, const void *);

>From 221f5b6067b3c874296bb05f0509deec3409147d Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Tue, 29 Sep 2026 19:47:57 +0530
Subject: [PATCH 4/5] [libc] Address review comments for alphasort test and
 headers

---
 libc/src/string/string_utils.h          |  1 -
 libc/test/src/dirent/alphasort_test.cpp | 28 +++++++++++++------------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h
index e3f7335ca6d8a..5e9e9ec20f77c 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -18,7 +18,6 @@
 #include "src/__support/CPP/bitset.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
-
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 #include "src/string/memory_utils/inline_memcpy.h"
 #include "src/string/string_length.h"
diff --git a/libc/test/src/dirent/alphasort_test.cpp b/libc/test/src/dirent/alphasort_test.cpp
index 11ef804a47b65..7e36f53587b53 100644
--- a/libc/test/src/dirent/alphasort_test.cpp
+++ b/libc/test/src/dirent/alphasort_test.cpp
@@ -15,19 +15,25 @@
 
 #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 {
-  alignas(struct dirent) char buf[sizeof(struct dirent) + 64]{};
+  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);
-    char *dst = d->d_name;
-    while (*name)
-      *dst++ = *name++;
-    *dst = '\0';
+    size_t len = LIBC_NAMESPACE::internal::string_length(name);
+    if (len > EXTRA_NAME_LEN)
+      len = EXTRA_NAME_LEN;
+    LIBC_NAMESPACE::inline_memcpy(d->d_name, name, len);
+    d->d_name[len] = '\0';
   }
 
   const struct dirent *get() const {
@@ -97,12 +103,8 @@ TEST(LlvmLibcAlphasortTest, QsortSorting) {
       entries, NUM_ENTRIES, sizeof(const struct dirent *),
       reinterpret_cast<QsortComparator>(LIBC_NAMESPACE::alphasort));
 
-  const struct dirent *e0 = entries[0];
-  const struct dirent *e1 = entries[1];
-  const struct dirent *e2 = entries[2];
-  const struct dirent *e3 = entries[3];
-
-  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e0, &e1), 0); // alpha < bravo
-  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e1, &e2), 0); // bravo < charlie
-  EXPECT_LT(LIBC_NAMESPACE::alphasort(&e2, &e3), 0); // charlie < delta
+  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");
 }

>From bbf672f21ed1a77ba5e8788a58fc5f1bfb0b47c6 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 30 Sep 2026 06:43:00 +0530
Subject: [PATCH 5/5] [libc] Address review feedback for alphasort test
 assertion

---
 libc/test/src/dirent/alphasort_test.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libc/test/src/dirent/alphasort_test.cpp b/libc/test/src/dirent/alphasort_test.cpp
index 7e36f53587b53..116a5a7c123e9 100644
--- a/libc/test/src/dirent/alphasort_test.cpp
+++ b/libc/test/src/dirent/alphasort_test.cpp
@@ -30,8 +30,9 @@ struct MockDirent {
   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)
-      len = EXTRA_NAME_LEN;
+      return;
     LIBC_NAMESPACE::inline_memcpy(d->d_name, name, len);
     d->d_name[len] = '\0';
   }



More information about the libc-commits mailing list