[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