[libc-commits] [libc] [llvm] [libc][stdlib] Implement heap sort. (PR #98582)

via libc-commits libc-commits at lists.llvm.org
Fri Jul 12 18:17:31 PDT 2024


https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/98582

>From b19bcbb794db159284cdd836ed28eed36eceb83b Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 12 Jul 2024 02:40:16 +0000
Subject: [PATCH 1/2] [libc][stdlib] Implement heap sort.

---
 .../modules/LLVMLibCCompileOptionRules.cmake  | 13 ++++-
 libc/config/baremetal/config.json             |  5 ++
 libc/config/config.json                       |  6 +++
 libc/docs/configure.rst                       |  2 +
 libc/src/stdlib/CMakeLists.txt                |  2 +
 libc/src/stdlib/heap_sort.h                   | 54 +++++++++++++++++++
 libc/src/stdlib/qsort.cpp                     | 12 ++++-
 libc/src/stdlib/qsort_r.cpp                   | 10 +++-
 libc/src/stdlib/qsort_util.h                  | 19 +++++--
 9 files changed, 114 insertions(+), 9 deletions(-)
 create mode 100644 libc/src/stdlib/heap_sort.h

diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index c5e7dfe8abd0f..097d9cb2fd1b9 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -34,10 +34,21 @@ function(_get_compile_options_from_flags output_var)
   set(${output_var} ${compile_options} PARENT_SCOPE)
 endfunction(_get_compile_options_from_flags)
 
+function(_get_compile_options_from_config output_var)
+  set(config_options "")
+
+  if(LIBC_CONF_QSORT_IMPL)
+    list(APPEND config_options "-DLIBC_QSORT_IMPL=${LIBC_CONF_QSORT_IMPL}")
+  endif()
+
+  set(${output_var} ${config_options} PARENT_SCOPE)
+endfunction(_get_compile_options_from_config)
+
 function(_get_common_compile_options output_var flags)
   _get_compile_options_from_flags(compile_flags ${flags})
+  _get_compile_options_from_config(config_flags)
 
-  set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${compile_flags})
+  set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${compile_flags} ${config_flags})
 
   if(LLVM_COMPILER_IS_GCC_COMPATIBLE)
     list(APPEND compile_options "-fpie")
diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json
index dda4c42425755..f2bc6e6517f7c 100644
--- a/libc/config/baremetal/config.json
+++ b/libc/config/baremetal/config.json
@@ -17,5 +17,10 @@
     "LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE": {
       "value": 102400
     }
+  },
+  "qsort": {
+    "LIBC_CONF_QSORT_IMPL": {
+      "value": "LIBC_QSORT_HEAP_SORT"
+    }
   }
 }
diff --git a/libc/config/config.json b/libc/config/config.json
index e8feab20175f4..e6a65d6689ec7 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -76,5 +76,11 @@
       "value": 0,
       "doc": "Configures optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, and LIBC_MATH_FAST."
     }
+  },
+  "qsort": {
+    "LIBC_CONF_QSORT_IMPL": {
+      "value": "LIBC_QSORT_QUICK_SORT",
+      "doc": "Configures sorting algorithm for qsort and qsort_r. Values accepted are LIBC_QSORT_QUICK_SORT, LIBC_QSORT_HEAP_SORT."
+    }
   }
 }
diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 9c641ef94570f..777330d815756 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -42,6 +42,8 @@ to learn about the defaults for your platform and target.
     - ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
     - ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).
     - ``LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY``: Automatically adjust timeout to CLOCK_MONOTONIC (default to true). POSIX API may require CLOCK_REALTIME, which can be unstable and leading to unexpected behavior. This option will convert the real-time timestamp to monotonic timestamp relative to the time of call.
+* **"qsort" options**
+    - ``LIBC_CONF_QSORT_IMPL``: Configures sorting algorithm for qsort and qsort_r. Values accepted are LIBC_QSORT_QUICK_SORT, LIBC_QSORT_HEAP_SORT.
 * **"scanf" options**
     - ``LIBC_CONF_SCANF_DISABLE_FLOAT``: Disable parsing floating point values in scanf and friends.
     - ``LIBC_CONF_SCANF_DISABLE_INDEX_MODE``: Disable index mode in the scanf format string.
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 7b7e55db391fa..e19d97b1025d9 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -259,8 +259,10 @@ add_header_library(
   qsort_util
   HDRS
     qsort_util.h
+    heap_sort.h
   DEPENDS
     libc.include.stdlib
+    libc.src.__support.CPP.cstddef
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdlib/heap_sort.h b/libc/src/stdlib/heap_sort.h
new file mode 100644
index 0000000000000..63aba88fb933f
--- /dev/null
+++ b/libc/src/stdlib/heap_sort.h
@@ -0,0 +1,54 @@
+//===-- Implementation of heap sort -----------------------------*- 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_STDLIB_HEAP_SORT_H
+#define LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
+
+#include "qsort_util.h"
+#include "src/__support/CPP/cstddef.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+// A simple in-place heapsort implementation.
+// Follow the implementation in https://en.wikipedia.org/wiki/Heapsort.
+
+LIBC_INLINE void heap_sort(const Array &array) {
+  size_t end = array.size();
+  size_t start = end / 2;
+
+  auto left_child = [](size_t i) -> size_t { return 2 * i + 1; };
+
+  while (end > 1) {
+    if (start > 0) {
+      --start;
+    } else {
+      --end;
+      array.swap(0, end);
+    }
+
+    size_t root = start;
+    while (left_child(root) < end) {
+      size_t child = left_child(root);
+      if (child + 1 < end &&
+          array.elem_compare(child, array.get(child + 1)) < 0)
+        ++child;
+
+      if (array.elem_compare(root, array.get(child)) >= 0)
+        break;
+
+      array.swap(root, child);
+      root = child;
+    }
+  }
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
diff --git a/libc/src/stdlib/qsort.cpp b/libc/src/stdlib/qsort.cpp
index 048e63ab214ed..fb6bfa006eb1f 100644
--- a/libc/src/stdlib/qsort.cpp
+++ b/libc/src/stdlib/qsort.cpp
@@ -9,6 +9,7 @@
 #include "src/stdlib/qsort.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/stdlib/heap_sort.h"
 #include "src/stdlib/qsort_util.h"
 
 #include <stdint.h>
@@ -21,8 +22,15 @@ LLVM_LIBC_FUNCTION(void, qsort,
   if (array == nullptr || array_size == 0 || elem_size == 0)
     return;
   internal::Comparator c(compare);
-  internal::quicksort(internal::Array(reinterpret_cast<uint8_t *>(array),
-                                      array_size, elem_size, c));
+
+  auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
+                             elem_size, c);
+
+#if LIBC_QSORT_IMPL == LIBC_QSORT_QUICK_SORT
+  internal::quick_sort(arr);
+#elif LIBC_QSORT_IMPL == LIBC_QSORT_HEAP_SORT
+  internal::heap_sort(arr);
+#endif
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/qsort_r.cpp b/libc/src/stdlib/qsort_r.cpp
index efbe5ad484b0e..3c9bb5aa21f43 100644
--- a/libc/src/stdlib/qsort_r.cpp
+++ b/libc/src/stdlib/qsort_r.cpp
@@ -22,8 +22,14 @@ LLVM_LIBC_FUNCTION(void, qsort_r,
   if (array == nullptr || array_size == 0 || elem_size == 0)
     return;
   internal::Comparator c(compare, arg);
-  internal::quicksort(internal::Array(reinterpret_cast<uint8_t *>(array),
-                                      array_size, elem_size, c));
+  auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
+                             elem_size, c);
+
+#if LIBC_QSORT_IMPL == LIBC_QSORT_QUICK_SORT
+  internal::quick_sort(arr);
+#elif LIBC_QSORT_IMPL == LIBC_QSORT_HEAP_SORT
+  internal::heap_sort(arr);
+#endif
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/qsort_util.h b/libc/src/stdlib/qsort_util.h
index 3a9cc4b8669f8..87945c26b3c93 100644
--- a/libc/src/stdlib/qsort_util.h
+++ b/libc/src/stdlib/qsort_util.h
@@ -12,7 +12,18 @@
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 #include <stdint.h>
-#include <stdlib.h>
+
+#define LIBC_QSORT_QUICK_SORT 1
+#define LIBC_QSORT_HEAP_SORT 2
+
+#ifndef LIBC_QSORT_IMPL
+#define LIBC_QSORT_IMPL LIBC_QSORT_QUICK_SORT
+#endif // LIBC_QSORT_IMPL
+
+#if (LIBC_QSORT_IMPL != LIBC_QSORT_QUICK_SORT &&                               \
+     LIBC_QSORT_IMPL != LIBC_QSORT_HEAP_SORT)
+#error "LIBC_QSORT_IMPL is not recognized."
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
@@ -136,7 +147,7 @@ static size_t partition(const Array &array) {
   }
 }
 
-LIBC_INLINE void quicksort(const Array &array) {
+LIBC_INLINE void quick_sort(const Array &array) {
   const size_t array_size = array.size();
   if (array_size <= 1)
     return;
@@ -145,8 +156,8 @@ LIBC_INLINE void quicksort(const Array &array) {
     // The partition operation sorts the two element array.
     return;
   }
-  quicksort(array.make_array(0, split_index));
-  quicksort(array.make_array(split_index, array.size() - split_index));
+  quick_sort(array.make_array(0, split_index));
+  quick_sort(array.make_array(split_index, array.size() - split_index));
 }
 
 } // namespace internal

>From 38facab9eaeb598a984b79f7cdd87e886ec34c71 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Sat, 13 Jul 2024 01:16:32 +0000
Subject: [PATCH 2/2] Address comments: Refactor qsort_util.h and add unit
 tests for quick_sort and heap_sort.

---
 libc/src/stdlib/CMakeLists.txt                |   2 +
 libc/src/stdlib/heap_sort.h                   |   9 +-
 libc/src/stdlib/qsort.cpp                     |   7 +-
 libc/src/stdlib/qsort_data.h                  | 102 +++++
 libc/src/stdlib/qsort_r.cpp                   |   6 +-
 libc/src/stdlib/qsort_util.h                  | 139 +------
 libc/src/stdlib/quick_sort.h                  |  78 ++++
 libc/test/src/stdlib/CMakeLists.txt           |  27 +-
 libc/test/src/stdlib/SortingTest.h            | 377 ++++++++++++++++++
 libc/test/src/stdlib/heap_sort_test.cpp       |  16 +
 libc/test/src/stdlib/qsort_test.cpp           | 258 +-----------
 libc/test/src/stdlib/quick_sort_test.cpp      |  16 +
 .../llvm-project-overlay/libc/BUILD.bazel     |   8 +-
 .../libc/test/src/stdlib/BUILD.bazel          |  25 ++
 14 files changed, 670 insertions(+), 400 deletions(-)
 create mode 100644 libc/src/stdlib/qsort_data.h
 create mode 100644 libc/src/stdlib/quick_sort.h
 create mode 100644 libc/test/src/stdlib/SortingTest.h
 create mode 100644 libc/test/src/stdlib/heap_sort_test.cpp
 create mode 100644 libc/test/src/stdlib/quick_sort_test.cpp

diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index e19d97b1025d9..513f6ad723d56 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -258,8 +258,10 @@ add_entrypoint_object(
 add_header_library(
   qsort_util
   HDRS
+    qsort_data.h
     qsort_util.h
     heap_sort.h
+    quick_sort.h
   DEPENDS
     libc.include.stdlib
     libc.src.__support.CPP.cstddef
diff --git a/libc/src/stdlib/heap_sort.h b/libc/src/stdlib/heap_sort.h
index 63aba88fb933f..ccb9ec5f82149 100644
--- a/libc/src/stdlib/heap_sort.h
+++ b/libc/src/stdlib/heap_sort.h
@@ -9,8 +9,8 @@
 #ifndef LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
 #define LLVM_LIBC_SRC_STDLIB_HEAP_SORT_H
 
-#include "qsort_util.h"
 #include "src/__support/CPP/cstddef.h"
+#include "src/stdlib/qsort_data.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
@@ -26,22 +26,29 @@ LIBC_INLINE void heap_sort(const Array &array) {
 
   while (end > 1) {
     if (start > 0) {
+      // Select the next unheapified element to sift down.
       --start;
     } else {
+      // Extract the max element of the heap, moving a leaf to root to be sifted
+      // down.
       --end;
       array.swap(0, end);
     }
 
+    // Sift start down the heap.
     size_t root = start;
     while (left_child(root) < end) {
       size_t child = left_child(root);
+      // If there are two children, set child to the greater.
       if (child + 1 < end &&
           array.elem_compare(child, array.get(child + 1)) < 0)
         ++child;
 
+      // If the root is less than the greater child
       if (array.elem_compare(root, array.get(child)) >= 0)
         break;
 
+      // Swap the root with the greater child and continue sifting down.
       array.swap(root, child);
       root = child;
     }
diff --git a/libc/src/stdlib/qsort.cpp b/libc/src/stdlib/qsort.cpp
index fb6bfa006eb1f..65a63c239f5c0 100644
--- a/libc/src/stdlib/qsort.cpp
+++ b/libc/src/stdlib/qsort.cpp
@@ -9,7 +9,6 @@
 #include "src/stdlib/qsort.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
-#include "src/stdlib/heap_sort.h"
 #include "src/stdlib/qsort_util.h"
 
 #include <stdint.h>
@@ -26,11 +25,7 @@ LLVM_LIBC_FUNCTION(void, qsort,
   auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
                              elem_size, c);
 
-#if LIBC_QSORT_IMPL == LIBC_QSORT_QUICK_SORT
-  internal::quick_sort(arr);
-#elif LIBC_QSORT_IMPL == LIBC_QSORT_HEAP_SORT
-  internal::heap_sort(arr);
-#endif
+  internal::sort(arr);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/qsort_data.h b/libc/src/stdlib/qsort_data.h
new file mode 100644
index 0000000000000..db045332708ae
--- /dev/null
+++ b/libc/src/stdlib/qsort_data.h
@@ -0,0 +1,102 @@
+//===-- Data structures for sorting routines --------------------*- 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_STDLIB_QSORT_DATA_H
+#define LLVM_LIBC_SRC_STDLIB_QSORT_DATA_H
+
+#include "src/__support/CPP/cstddef.h"
+#include "src/__support/macros/config.h"
+
+#include <stdint.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+using Compare = int(const void *, const void *);
+using CompareWithState = int(const void *, const void *, void *);
+
+enum class CompType { COMPARE, COMPARE_WITH_STATE };
+
+struct Comparator {
+  union {
+    Compare *comp_func;
+    CompareWithState *comp_func_r;
+  };
+  const CompType comp_type;
+
+  void *arg;
+
+  Comparator(Compare *func)
+      : comp_func(func), comp_type(CompType::COMPARE), arg(nullptr) {}
+
+  Comparator(CompareWithState *func, void *arg_val)
+      : comp_func_r(func), comp_type(CompType::COMPARE_WITH_STATE),
+        arg(arg_val) {}
+
+#if defined(__clang__)
+  // Recent upstream changes to -fsanitize=function find more instances of
+  // function type mismatches. One case is with the comparator passed to this
+  // class. Libraries will tend to pass comparators that take pointers to
+  // varying types while this comparator expects to accept const void pointers.
+  // Ideally those tools would pass a function that strictly accepts const
+  // void*s to avoid UB, or would use qsort_r to pass their own comparator.
+  [[clang::no_sanitize("function")]]
+#endif
+  int comp_vals(const void *a, const void *b) const {
+    if (comp_type == CompType::COMPARE) {
+      return comp_func(a, b);
+    } else {
+      return comp_func_r(a, b, arg);
+    }
+  }
+};
+
+class Array {
+  uint8_t *array;
+  size_t array_size;
+  size_t elem_size;
+  Comparator compare;
+
+public:
+  Array(uint8_t *a, size_t s, size_t e, Comparator c)
+      : array(a), array_size(s), elem_size(e), compare(c) {}
+
+  uint8_t *get(size_t i) const { return array + i * elem_size; }
+
+  void swap(size_t i, size_t j) const {
+    uint8_t *elem_i = get(i);
+    uint8_t *elem_j = get(j);
+    for (size_t b = 0; b < elem_size; ++b) {
+      uint8_t temp = elem_i[b];
+      elem_i[b] = elem_j[b];
+      elem_j[b] = temp;
+    }
+  }
+
+  int elem_compare(size_t i, const uint8_t *other) const {
+    // An element must compare equal to itself so we don't need to consult the
+    // user provided comparator.
+    if (get(i) == other)
+      return 0;
+    return compare.comp_vals(get(i), other);
+  }
+
+  size_t size() const { return array_size; }
+
+  // Make an Array starting at index |i| and size |s|.
+  Array make_array(size_t i, size_t s) const {
+    return Array(get(i), s, elem_size, compare);
+  }
+};
+
+using SortingRoutine = void(const Array &);
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_QSORT_DATA_H
diff --git a/libc/src/stdlib/qsort_r.cpp b/libc/src/stdlib/qsort_r.cpp
index 3c9bb5aa21f43..bf61a40e84734 100644
--- a/libc/src/stdlib/qsort_r.cpp
+++ b/libc/src/stdlib/qsort_r.cpp
@@ -25,11 +25,7 @@ LLVM_LIBC_FUNCTION(void, qsort_r,
   auto arr = internal::Array(reinterpret_cast<uint8_t *>(array), array_size,
                              elem_size, c);
 
-#if LIBC_QSORT_IMPL == LIBC_QSORT_QUICK_SORT
-  internal::quick_sort(arr);
-#elif LIBC_QSORT_IMPL == LIBC_QSORT_HEAP_SORT
-  internal::heap_sort(arr);
-#endif
+  internal::sort(arr);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/qsort_util.h b/libc/src/stdlib/qsort_util.h
index 87945c26b3c93..d42adde06d976 100644
--- a/libc/src/stdlib/qsort_util.h
+++ b/libc/src/stdlib/qsort_util.h
@@ -9,9 +9,8 @@
 #ifndef LLVM_LIBC_SRC_STDLIB_QSORT_UTIL_H
 #define LLVM_LIBC_SRC_STDLIB_QSORT_UTIL_H
 
-#include "src/__support/macros/attributes.h"
-#include "src/__support/macros/config.h"
-#include <stdint.h>
+#include "src/stdlib/heap_sort.h"
+#include "src/stdlib/quick_sort.h"
 
 #define LIBC_QSORT_QUICK_SORT 1
 #define LIBC_QSORT_HEAP_SORT 2
@@ -28,137 +27,11 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
-// A simple quicksort implementation using the Hoare partition scheme.
-
-using Compare = int(const void *, const void *);
-using CompareWithState = int(const void *, const void *, void *);
-
-enum class CompType { COMPARE, COMPARE_WITH_STATE };
-
-struct Comparator {
-  union {
-    Compare *comp_func;
-    CompareWithState *comp_func_r;
-  };
-  const CompType comp_type;
-
-  void *arg;
-
-  Comparator(Compare *func)
-      : comp_func(func), comp_type(CompType::COMPARE), arg(nullptr) {}
-
-  Comparator(CompareWithState *func, void *arg_val)
-      : comp_func_r(func), comp_type(CompType::COMPARE_WITH_STATE),
-        arg(arg_val) {}
-
-#if defined(__clang__)
-  // Recent upstream changes to -fsanitize=function find more instances of
-  // function type mismatches. One case is with the comparator passed to this
-  // class. Libraries will tend to pass comparators that take pointers to
-  // varying types while this comparator expects to accept const void pointers.
-  // Ideally those tools would pass a function that strictly accepts const
-  // void*s to avoid UB, or would use qsort_r to pass their own comparator.
-  [[clang::no_sanitize("function")]]
+#if LIBC_QSORT_IMPL == LIBC_QSORT_QUICK_SORT
+constexpr auto sort = quick_sort;
+#elif LIBC_QSORT_IMPL == LIBC_QSORT_HEAP_SORT
+constexpr auto sort = heap_sort;
 #endif
-  int comp_vals(const void *a, const void *b) const {
-    if (comp_type == CompType::COMPARE) {
-      return comp_func(a, b);
-    } else {
-      return comp_func_r(a, b, arg);
-    }
-  }
-};
-
-class Array {
-  uint8_t *array;
-  size_t array_size;
-  size_t elem_size;
-  Comparator compare;
-
-public:
-  Array(uint8_t *a, size_t s, size_t e, Comparator c)
-      : array(a), array_size(s), elem_size(e), compare(c) {}
-
-  uint8_t *get(size_t i) const { return array + i * elem_size; }
-
-  void swap(size_t i, size_t j) const {
-    uint8_t *elem_i = get(i);
-    uint8_t *elem_j = get(j);
-    for (size_t b = 0; b < elem_size; ++b) {
-      uint8_t temp = elem_i[b];
-      elem_i[b] = elem_j[b];
-      elem_j[b] = temp;
-    }
-  }
-
-  int elem_compare(size_t i, const uint8_t *other) const {
-    // An element must compare equal to itself so we don't need to consult the
-    // user provided comparator.
-    if (get(i) == other)
-      return 0;
-    return compare.comp_vals(get(i), other);
-  }
-
-  size_t size() const { return array_size; }
-
-  // Make an Array starting at index |i| and size |s|.
-  Array make_array(size_t i, size_t s) const {
-    return Array(get(i), s, elem_size, compare);
-  }
-};
-
-static size_t partition(const Array &array) {
-  const size_t array_size = array.size();
-  size_t pivot_index = array_size / 2;
-  uint8_t *pivot = array.get(pivot_index);
-  size_t i = 0;
-  size_t j = array_size - 1;
-
-  while (true) {
-    int compare_i, compare_j;
-
-    while ((compare_i = array.elem_compare(i, pivot)) < 0)
-      ++i;
-    while ((compare_j = array.elem_compare(j, pivot)) > 0)
-      --j;
-
-    // At some point i will crossover j so we will definitely break out of
-    // this while loop.
-    if (i >= j)
-      return j + 1;
-
-    array.swap(i, j);
-
-    // The pivot itself might have got swapped so we will update the pivot.
-    if (i == pivot_index) {
-      pivot = array.get(j);
-      pivot_index = j;
-    } else if (j == pivot_index) {
-      pivot = array.get(i);
-      pivot_index = i;
-    }
-
-    if (compare_i == 0 && compare_j == 0) {
-      // If we do not move the pointers, we will end up with an
-      // infinite loop as i and j will be stuck without advancing.
-      ++i;
-      --j;
-    }
-  }
-}
-
-LIBC_INLINE void quick_sort(const Array &array) {
-  const size_t array_size = array.size();
-  if (array_size <= 1)
-    return;
-  size_t split_index = partition(array);
-  if (array_size <= 2) {
-    // The partition operation sorts the two element array.
-    return;
-  }
-  quick_sort(array.make_array(0, split_index));
-  quick_sort(array.make_array(split_index, array.size() - split_index));
-}
 
 } // namespace internal
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/quick_sort.h b/libc/src/stdlib/quick_sort.h
new file mode 100644
index 0000000000000..89ec107161e3e
--- /dev/null
+++ b/libc/src/stdlib/quick_sort.h
@@ -0,0 +1,78 @@
+//===-- Implementation header for qsort utilities ---------------*- 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_STDLIB_QUICK_SORT_H
+#define LLVM_LIBC_SRC_STDLIB_QUICK_SORT_H
+
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/stdlib/qsort_data.h"
+
+#include <stdint.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+// A simple quicksort implementation using the Hoare partition scheme.
+static size_t partition(const Array &array) {
+  const size_t array_size = array.size();
+  size_t pivot_index = array_size / 2;
+  uint8_t *pivot = array.get(pivot_index);
+  size_t i = 0;
+  size_t j = array_size - 1;
+
+  while (true) {
+    int compare_i, compare_j;
+
+    while ((compare_i = array.elem_compare(i, pivot)) < 0)
+      ++i;
+    while ((compare_j = array.elem_compare(j, pivot)) > 0)
+      --j;
+
+    // At some point i will crossover j so we will definitely break out of
+    // this while loop.
+    if (i >= j)
+      return j + 1;
+
+    array.swap(i, j);
+
+    // The pivot itself might have got swapped so we will update the pivot.
+    if (i == pivot_index) {
+      pivot = array.get(j);
+      pivot_index = j;
+    } else if (j == pivot_index) {
+      pivot = array.get(i);
+      pivot_index = i;
+    }
+
+    if (compare_i == 0 && compare_j == 0) {
+      // If we do not move the pointers, we will end up with an
+      // infinite loop as i and j will be stuck without advancing.
+      ++i;
+      --j;
+    }
+  }
+}
+
+LIBC_INLINE void quick_sort(const Array &array) {
+  const size_t array_size = array.size();
+  if (array_size <= 1)
+    return;
+  size_t split_index = partition(array);
+  if (array_size <= 2) {
+    // The partition operation sorts the two element array.
+    return;
+  }
+  quick_sort(array.make_array(0, split_index));
+  quick_sort(array.make_array(split_index, array.size() - split_index));
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_QUICK_SORT_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 38488778c657c..db90d9a4741eb 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -290,14 +290,39 @@ add_libc_test(
     libc.src.stdlib.bsearch
 )
 
+add_libc_test(
+  quick_sort_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    quick_sort_test.cpp
+  HDRS
+    SortingTest.h
+  DEPENDS
+    libc.src.stdlib.qsort_util
+)
+
+add_libc_test(
+  heap_sort_test
+  SUITE
+    libc-stdlib-tests
+  SRCS
+    heap_sort_test.cpp
+  HDRS
+    SortingTest.h
+  DEPENDS
+    libc.src.stdlib.qsort_util
+)
+
 add_libc_test(
   qsort_test
   SUITE
     libc-stdlib-tests
   SRCS
     qsort_test.cpp
+  HDRS
+    SortingTest.h
   DEPENDS
-    libc.include.stdlib
     libc.src.stdlib.qsort
 )
 
diff --git a/libc/test/src/stdlib/SortingTest.h b/libc/test/src/stdlib/SortingTest.h
new file mode 100644
index 0000000000000..d34584e5addf0
--- /dev/null
+++ b/libc/test/src/stdlib/SortingTest.h
@@ -0,0 +1,377 @@
+//===-- Unittests for sorting routines ------------------------------------===//
+//
+// 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/__support/macros/config.h"
+#include "src/stdlib/qsort_data.h"
+#include "test/UnitTest/Test.h"
+
+class SortingTest : public LIBC_NAMESPACE::testing::Test {
+
+  using Array = LIBC_NAMESPACE::internal::Array;
+  using Comparator = LIBC_NAMESPACE::internal::Comparator;
+  using SortingRoutine = LIBC_NAMESPACE::internal::SortingRoutine;
+
+public:
+  static int int_compare(const void *l, const void *r) {
+    int li = *reinterpret_cast<const int *>(l);
+    int ri = *reinterpret_cast<const int *>(r);
+    if (li == ri)
+      return 0;
+    else if (li > ri)
+      return 1;
+    else
+      return -1;
+  }
+
+  void test_sorted_array(SortingRoutine sort_func) {
+    int array[25] = {10,   23,   33,   35,   55,   70,    71,   100,  110,
+                     123,  133,  135,  155,  170,  171,   1100, 1110, 1123,
+                     1133, 1135, 1155, 1170, 1171, 11100, 12310};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_LE(array[0], 10);
+    ASSERT_LE(array[1], 23);
+    ASSERT_LE(array[2], 33);
+    ASSERT_LE(array[3], 35);
+    ASSERT_LE(array[4], 55);
+    ASSERT_LE(array[5], 70);
+    ASSERT_LE(array[6], 71);
+    ASSERT_LE(array[7], 100);
+    ASSERT_LE(array[8], 110);
+    ASSERT_LE(array[9], 123);
+    ASSERT_LE(array[10], 133);
+    ASSERT_LE(array[11], 135);
+    ASSERT_LE(array[12], 155);
+    ASSERT_LE(array[13], 170);
+    ASSERT_LE(array[14], 171);
+    ASSERT_LE(array[15], 1100);
+    ASSERT_LE(array[16], 1110);
+    ASSERT_LE(array[17], 1123);
+    ASSERT_LE(array[18], 1133);
+    ASSERT_LE(array[19], 1135);
+    ASSERT_LE(array[20], 1155);
+    ASSERT_LE(array[21], 1170);
+    ASSERT_LE(array[22], 1171);
+    ASSERT_LE(array[23], 11100);
+    ASSERT_LE(array[24], 12310);
+  }
+
+  void test_reversed_sorted_array(SortingRoutine sort_func) {
+    int array[] = {25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
+                   12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    for (int i = 0; i < int(ARRAY_SIZE - 1); ++i)
+      ASSERT_EQ(array[i], i + 1);
+  }
+
+  void test_all_equal_elements(SortingRoutine sort_func) {
+    int array[] = {100, 100, 100, 100, 100, 100, 100, 100, 100,
+                   100, 100, 100, 100, 100, 100, 100, 100, 100,
+                   100, 100, 100, 100, 100, 100, 100};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    for (size_t i = 0; i < ARRAY_SIZE; ++i)
+      ASSERT_EQ(array[i], 100);
+  }
+
+  void test_unsorted_array_1(SortingRoutine sort_func) {
+    int array[25] = {10,  23,  8,    35,   55,   45,  40,  100, 110,
+                     123, 90,  80,   70,   60,   171, 11,  1,   -1,
+                     -5,  -10, 1155, 1170, 1171, 12,  -100};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], -100);
+    ASSERT_EQ(array[1], -10);
+    ASSERT_EQ(array[2], -5);
+    ASSERT_EQ(array[3], -1);
+    ASSERT_EQ(array[4], 1);
+    ASSERT_EQ(array[5], 8);
+    ASSERT_EQ(array[6], 10);
+    ASSERT_EQ(array[7], 11);
+    ASSERT_EQ(array[8], 12);
+    ASSERT_EQ(array[9], 23);
+    ASSERT_EQ(array[10], 35);
+    ASSERT_EQ(array[11], 40);
+    ASSERT_EQ(array[12], 45);
+    ASSERT_EQ(array[13], 55);
+    ASSERT_EQ(array[14], 60);
+    ASSERT_EQ(array[15], 70);
+    ASSERT_EQ(array[16], 80);
+    ASSERT_EQ(array[17], 90);
+    ASSERT_EQ(array[18], 100);
+    ASSERT_EQ(array[19], 110);
+    ASSERT_EQ(array[20], 123);
+    ASSERT_EQ(array[21], 171);
+    ASSERT_EQ(array[22], 1155);
+    ASSERT_EQ(array[23], 1170);
+    ASSERT_EQ(array[24], 1171);
+  }
+
+  void test_unsorted_array_2(SortingRoutine sort_func) {
+    int array[7] = {10, 40, 45, 55, 35, 23, 60};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 10);
+    ASSERT_EQ(array[1], 23);
+    ASSERT_EQ(array[2], 35);
+    ASSERT_EQ(array[3], 40);
+    ASSERT_EQ(array[4], 45);
+    ASSERT_EQ(array[5], 55);
+    ASSERT_EQ(array[6], 60);
+  }
+
+  void test_unsorted_array_duplicated_1(SortingRoutine sort_func) {
+    int array[6] = {10, 10, 20, 20, 5, 5};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 5);
+    ASSERT_EQ(array[1], 5);
+    ASSERT_EQ(array[2], 10);
+    ASSERT_EQ(array[3], 10);
+    ASSERT_EQ(array[4], 20);
+    ASSERT_EQ(array[5], 20);
+  }
+
+  void test_unsorted_array_duplicated_2(SortingRoutine sort_func) {
+    int array[10] = {20, 10, 10, 10, 10, 20, 21, 21, 21, 21};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 10);
+    ASSERT_EQ(array[1], 10);
+    ASSERT_EQ(array[2], 10);
+    ASSERT_EQ(array[3], 10);
+    ASSERT_EQ(array[4], 20);
+    ASSERT_EQ(array[5], 20);
+    ASSERT_EQ(array[6], 21);
+    ASSERT_EQ(array[7], 21);
+    ASSERT_EQ(array[8], 21);
+    ASSERT_EQ(array[9], 21);
+  }
+
+  void test_unsorted_array_duplicated_3(SortingRoutine sort_func) {
+    int array[10] = {20, 30, 30, 30, 30, 20, 21, 21, 21, 21};
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 20);
+    ASSERT_EQ(array[1], 20);
+    ASSERT_EQ(array[2], 21);
+    ASSERT_EQ(array[3], 21);
+    ASSERT_EQ(array[4], 21);
+    ASSERT_EQ(array[5], 21);
+    ASSERT_EQ(array[6], 30);
+    ASSERT_EQ(array[7], 30);
+    ASSERT_EQ(array[8], 30);
+    ASSERT_EQ(array[9], 30);
+  }
+
+  void test_unsorted_three_element_1(SortingRoutine sort_func) {
+    int array[3] = {14999024, 0, 3};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 0);
+    ASSERT_EQ(array[1], 3);
+    ASSERT_EQ(array[2], 14999024);
+  }
+
+  void test_unsorted_three_element_2(SortingRoutine sort_func) {
+    int array[3] = {3, 14999024, 0};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 0);
+    ASSERT_EQ(array[1], 3);
+    ASSERT_EQ(array[2], 14999024);
+  }
+
+  void test_unsorted_three_element_3(SortingRoutine sort_func) {
+    int array[3] = {3, 0, 14999024};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 0);
+    ASSERT_EQ(array[1], 3);
+    ASSERT_EQ(array[2], 14999024);
+  }
+
+  void test_same_three_element(SortingRoutine sort_func) {
+    int array[3] = {12345, 12345, 12345};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 12345);
+    ASSERT_EQ(array[1], 12345);
+    ASSERT_EQ(array[2], 12345);
+  }
+
+  void test_unsorted_two_element_1(SortingRoutine sort_func) {
+    int array[] = {14999024, 0};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 0);
+    ASSERT_EQ(array[1], 14999024);
+  }
+
+  void test_unsorted_two_element_2(SortingRoutine sort_func) {
+    int array[] = {0, 14999024};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 0);
+    ASSERT_EQ(array[1], 14999024);
+  }
+
+  void test_same_two_element(SortingRoutine sort_func) {
+    int array[] = {12345, 12345};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 12345);
+    ASSERT_EQ(array[1], 12345);
+  }
+
+  void test_single_element(SortingRoutine sort_func) {
+    int array[] = {12345};
+
+    constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
+
+    auto arr = Array(reinterpret_cast<uint8_t *>(array), ARRAY_SIZE,
+                     sizeof(int), Comparator(int_compare));
+
+    sort_func(arr);
+
+    ASSERT_EQ(array[0], 12345);
+  }
+};
+
+#define LIST_SORTING_TESTS(Name, Func)                                         \
+  using LlvmLibc##Name##Test = SortingTest;                                    \
+  TEST_F(LlvmLibc##Name##Test, SortedArray) { test_sorted_array(Func); }       \
+  TEST_F(LlvmLibc##Name##Test, ReverseSortedArray) {                           \
+    test_reversed_sorted_array(Func);                                          \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, AllEqualElements) {                             \
+    test_all_equal_elements(Func);                                             \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedArray1) {                               \
+    test_unsorted_array_1(Func);                                               \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedArray2) {                               \
+    test_unsorted_array_2(Func);                                               \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedArrayDuplicateElements1) {              \
+    test_unsorted_array_duplicated_1(Func);                                    \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedArrayDuplicateElements2) {              \
+    test_unsorted_array_duplicated_2(Func);                                    \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedArrayDuplicateElements3) {              \
+    test_unsorted_array_duplicated_3(Func);                                    \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedThreeElementArray1) {                   \
+    test_unsorted_three_element_1(Func);                                       \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedThreeElementArray2) {                   \
+    test_unsorted_three_element_2(Func);                                       \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedThreeElementArray3) {                   \
+    test_unsorted_three_element_3(Func);                                       \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, SameElementThreeElementArray) {                 \
+    test_same_three_element(Func);                                             \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedTwoElementArray1) {                     \
+    test_unsorted_two_element_1(Func);                                         \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, UnsortedTwoElementArray2) {                     \
+    test_unsorted_two_element_2(Func);                                         \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, SameElementTwoElementArray) {                   \
+    test_same_two_element(Func);                                               \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Test, SingleElementArray) {                           \
+    test_single_element(Func);                                                 \
+  }                                                                            \
+  static_assert(true)
diff --git a/libc/test/src/stdlib/heap_sort_test.cpp b/libc/test/src/stdlib/heap_sort_test.cpp
new file mode 100644
index 0000000000000..d70e3dc2272be
--- /dev/null
+++ b/libc/test/src/stdlib/heap_sort_test.cpp
@@ -0,0 +1,16 @@
+//===-- Unittests for heap sort -------------------------------------------===//
+//
+// 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 "SortingTest.h"
+#include "src/stdlib/heap_sort.h"
+
+void sort(const LIBC_NAMESPACE::internal::Array &array) {
+  LIBC_NAMESPACE::internal::heap_sort(array);
+}
+
+LIST_SORTING_TESTS(HeapSort, sort);
diff --git a/libc/test/src/stdlib/qsort_test.cpp b/libc/test/src/stdlib/qsort_test.cpp
index 0822d490e6520..1e921a86fd1fd 100644
--- a/libc/test/src/stdlib/qsort_test.cpp
+++ b/libc/test/src/stdlib/qsort_test.cpp
@@ -6,260 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SortingTest.h"
 #include "src/stdlib/qsort.h"
 
-#include "test/UnitTest/Test.h"
-
-#include <stdlib.h>
-
-static int int_compare(const void *l, const void *r) {
-  int li = *reinterpret_cast<const int *>(l);
-  int ri = *reinterpret_cast<const int *>(r);
-  if (li == ri)
-    return 0;
-  else if (li > ri)
-    return 1;
-  else
-    return -1;
+void sort(const LIBC_NAMESPACE::internal::Array &array) {
+  LIBC_NAMESPACE::qsort(reinterpret_cast<void *>(array.get(0)), array.size(),
+                        sizeof(int), SortingTest::int_compare);
 }
 
-TEST(LlvmLibcQsortTest, SortedArray) {
-  int array[25] = {10,   23,   33,   35,   55,   70,    71,   100,  110,
-                   123,  133,  135,  155,  170,  171,   1100, 1110, 1123,
-                   1133, 1135, 1155, 1170, 1171, 11100, 12310};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 10);
-  ASSERT_LE(array[1], 23);
-  ASSERT_LE(array[2], 33);
-  ASSERT_LE(array[3], 35);
-  ASSERT_LE(array[4], 55);
-  ASSERT_LE(array[5], 70);
-  ASSERT_LE(array[6], 71);
-  ASSERT_LE(array[7], 100);
-  ASSERT_LE(array[8], 110);
-  ASSERT_LE(array[9], 123);
-  ASSERT_LE(array[10], 133);
-  ASSERT_LE(array[11], 135);
-  ASSERT_LE(array[12], 155);
-  ASSERT_LE(array[13], 170);
-  ASSERT_LE(array[14], 171);
-  ASSERT_LE(array[15], 1100);
-  ASSERT_LE(array[16], 1110);
-  ASSERT_LE(array[17], 1123);
-  ASSERT_LE(array[18], 1133);
-  ASSERT_LE(array[19], 1135);
-  ASSERT_LE(array[20], 1155);
-  ASSERT_LE(array[21], 1170);
-  ASSERT_LE(array[22], 1171);
-  ASSERT_LE(array[23], 11100);
-  ASSERT_LE(array[24], 12310);
-}
-
-TEST(LlvmLibcQsortTest, ReverseSortedArray) {
-  int array[25] = {25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
-                   12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  for (int i = 0; i < int(ARRAY_SIZE - 1); ++i)
-    ASSERT_LE(array[i], i + 1);
-}
-
-TEST(LlvmLibcQsortTest, AllEqualElements) {
-  int array[25] = {100, 100, 100, 100, 100, 100, 100, 100, 100,
-                   100, 100, 100, 100, 100, 100, 100, 100, 100,
-                   100, 100, 100, 100, 100, 100, 100};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  for (size_t i = 0; i < ARRAY_SIZE - 1; ++i)
-    ASSERT_LE(array[i], 100);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedArray1) {
-  int array[25] = {10, 23,  8,  35, 55, 45, 40,  100,  110,  123,  90, 80,  70,
-                   60, 171, 11, 1,  -1, -5, -10, 1155, 1170, 1171, 12, -100};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], -100);
-  ASSERT_LE(array[1], -10);
-  ASSERT_LE(array[2], -5);
-  ASSERT_LE(array[3], -1);
-  ASSERT_LE(array[4], 1);
-  ASSERT_LE(array[5], 8);
-  ASSERT_LE(array[6], 10);
-  ASSERT_LE(array[7], 11);
-  ASSERT_LE(array[8], 12);
-  ASSERT_LE(array[9], 23);
-  ASSERT_LE(array[10], 35);
-  ASSERT_LE(array[11], 40);
-  ASSERT_LE(array[12], 45);
-  ASSERT_LE(array[13], 55);
-  ASSERT_LE(array[14], 60);
-  ASSERT_LE(array[15], 70);
-  ASSERT_LE(array[16], 80);
-  ASSERT_LE(array[17], 90);
-  ASSERT_LE(array[18], 100);
-  ASSERT_LE(array[19], 110);
-  ASSERT_LE(array[20], 123);
-  ASSERT_LE(array[21], 171);
-  ASSERT_LE(array[22], 1155);
-  ASSERT_LE(array[23], 1170);
-  ASSERT_LE(array[24], 1171);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedArray2) {
-  int array[7] = {10, 40, 45, 55, 35, 23, 60};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 10);
-  ASSERT_LE(array[1], 23);
-  ASSERT_LE(array[2], 35);
-  ASSERT_LE(array[3], 40);
-  ASSERT_LE(array[4], 45);
-  ASSERT_LE(array[5], 55);
-  ASSERT_LE(array[6], 60);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedArrayDuplicateElements1) {
-  int array[6] = {10, 10, 20, 20, 5, 5};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 5);
-  ASSERT_LE(array[1], 5);
-  ASSERT_LE(array[2], 10);
-  ASSERT_LE(array[3], 10);
-  ASSERT_LE(array[4], 20);
-  ASSERT_LE(array[5], 20);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedArrayDuplicateElements2) {
-  int array[10] = {20, 10, 10, 10, 10, 20, 21, 21, 21, 21};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 10);
-  ASSERT_LE(array[1], 10);
-  ASSERT_LE(array[2], 10);
-  ASSERT_LE(array[3], 10);
-  ASSERT_LE(array[4], 20);
-  ASSERT_LE(array[5], 20);
-  ASSERT_LE(array[6], 21);
-  ASSERT_LE(array[7], 21);
-  ASSERT_LE(array[8], 21);
-  ASSERT_LE(array[9], 21);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedArrayDuplicateElements3) {
-  int array[10] = {20, 30, 30, 30, 30, 20, 21, 21, 21, 21};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 20);
-  ASSERT_LE(array[1], 20);
-  ASSERT_LE(array[2], 21);
-  ASSERT_LE(array[3], 21);
-  ASSERT_LE(array[4], 21);
-  ASSERT_LE(array[5], 21);
-  ASSERT_LE(array[6], 30);
-  ASSERT_LE(array[7], 30);
-  ASSERT_LE(array[8], 30);
-  ASSERT_LE(array[9], 30);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedThreeElementArray1) {
-  int array[3] = {14999024, 0, 3};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 0);
-  ASSERT_LE(array[1], 3);
-  ASSERT_LE(array[2], 14999024);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedThreeElementArray2) {
-  int array[3] = {3, 14999024, 0};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 0);
-  ASSERT_LE(array[1], 3);
-  ASSERT_LE(array[2], 14999024);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedThreeElementArray3) {
-  int array[3] = {3, 0, 14999024};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 0);
-  ASSERT_LE(array[1], 3);
-  ASSERT_LE(array[2], 14999024);
-}
-
-TEST(LlvmLibcQsortTest, SameElementThreeElementArray) {
-  int array[3] = {12345, 12345, 12345};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 12345);
-  ASSERT_LE(array[1], 12345);
-  ASSERT_LE(array[2], 12345);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedTwoElementArray1) {
-  int array[2] = {14999024, 0};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 0);
-  ASSERT_LE(array[1], 14999024);
-}
-
-TEST(LlvmLibcQsortTest, UnsortedTwoElementArray2) {
-  int array[2] = {0, 14999024};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 0);
-  ASSERT_LE(array[1], 14999024);
-}
-
-TEST(LlvmLibcQsortTest, SameElementTwoElementArray) {
-  int array[2] = {12345, 12345};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], 12345);
-  ASSERT_LE(array[1], 12345);
-}
-
-TEST(LlvmLibcQSortTest, SingleElementArray) {
-  constexpr int ELEM = 12345;
-  int array[1] = {ELEM};
-  constexpr size_t ARRAY_SIZE = sizeof(array) / sizeof(int);
-
-  LIBC_NAMESPACE::qsort(array, ARRAY_SIZE, sizeof(int), int_compare);
-
-  ASSERT_LE(array[0], ELEM);
-}
+LIST_SORTING_TESTS(Qsort, sort);
diff --git a/libc/test/src/stdlib/quick_sort_test.cpp b/libc/test/src/stdlib/quick_sort_test.cpp
new file mode 100644
index 0000000000000..d6bf77ebfd40d
--- /dev/null
+++ b/libc/test/src/stdlib/quick_sort_test.cpp
@@ -0,0 +1,16 @@
+//===-- Unittests for quick sort ------------------------------------------===//
+//
+// 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 "SortingTest.h"
+#include "src/stdlib/quick_sort.h"
+
+void sort(const LIBC_NAMESPACE::internal::Array &array) {
+  LIBC_NAMESPACE::internal::quick_sort(array);
+}
+
+LIST_SORTING_TESTS(QuickSort, sort);
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 7f9a114d95775..750f14d1a649c 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2396,9 +2396,15 @@ libc_function(
 
 libc_support_library(
     name = "qsort_util",
-    hdrs = ["src/stdlib/qsort_util.h"],
+    hdrs = [
+        "src/stdlib/heap_sort.h",
+        "src/stdlib/qsort_data.h",
+        "src/stdlib/qsort_util.h",
+        "src/stdlib/quick_sort.h",
+    ],
     deps = [
         ":__support_common",
+        ":__support_cpp_cstddef",
         ":__support_macros_attributes",
     ],
 )
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/stdlib/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/stdlib/BUILD.bazel
index b34e281ce0ecd..a6f9d4f2fdac2 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/stdlib/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/stdlib/BUILD.bazel
@@ -100,10 +100,35 @@ libc_test(
     libc_function_deps = ["//libc:bsearch"],
 )
 
+libc_support_library(
+    name = "qsort_test_helper",
+    hdrs = ["SortingTest.h"],
+    deps = [
+        "//libc:qsort_util",
+        "//libc/test/UnitTest:LibcUnitTest",
+    ],
+)
 libc_test(
     name = "qsort_test",
     srcs = ["qsort_test.cpp"],
     libc_function_deps = ["//libc:qsort"],
+    deps = [":qsort_test_helper"],
+)
+libc_test(
+    name = "quick_sort_test",
+    srcs = ["quick_sort_test.cpp"],
+    deps = [
+        ":qsort_test_helper",
+        "//libc:qsort_util",
+    ],
+)
+libc_test(
+    name = "heap_sort_test",
+    srcs = ["heap_sort_test.cpp"],
+    deps = [
+        ":qsort_test_helper",
+        "//libc:qsort_util",
+    ],
 )
 
 libc_test(



More information about the libc-commits mailing list