1 //===-- Implementation header for qsort utilities ---------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_STDLIB_QSORT_UTIL_H
10 #define LLVM_LIBC_SRC_STDLIB_QSORT_UTIL_H
12 #include "src/__support/macros/attributes.h"
16 namespace LIBC_NAMESPACE::internal
{
18 // A simple quicksort implementation using the Hoare partition scheme.
20 using Compare
= int(const void *, const void *);
21 using CompareWithState
= int(const void *, const void *, void *);
23 enum class CompType
{ COMPARE
, COMPARE_WITH_STATE
};
28 CompareWithState
*comp_func_r
;
30 const CompType comp_type
;
34 Comparator(Compare
*func
)
35 : comp_func(func
), comp_type(CompType::COMPARE
), arg(nullptr) {}
37 Comparator(CompareWithState
*func
, void *arg_val
)
38 : comp_func_r(func
), comp_type(CompType::COMPARE_WITH_STATE
),
41 #if defined(__clang__)
42 // Recent upstream changes to -fsanitize=function find more instances of
43 // function type mismatches. One case is with the comparator passed to this
44 // class. Libraries will tend to pass comparators that take pointers to
45 // varying types while this comparator expects to accept const void pointers.
46 // Ideally those tools would pass a function that strictly accepts const
47 // void*s to avoid UB, or would use qsort_r to pass their own comparator.
48 [[clang::no_sanitize("function")]]
50 int comp_vals(const void *a
, const void *b
) const {
51 if (comp_type
== CompType::COMPARE
) {
52 return comp_func(a
, b
);
54 return comp_func_r(a
, b
, arg
);
66 Array(uint8_t *a
, size_t s
, size_t e
, Comparator c
)
67 : array(a
), array_size(s
), elem_size(e
), compare(c
) {}
69 uint8_t *get(size_t i
) const { return array
+ i
* elem_size
; }
71 void swap(size_t i
, size_t j
) const {
72 uint8_t *elem_i
= get(i
);
73 uint8_t *elem_j
= get(j
);
74 for (size_t b
= 0; b
< elem_size
; ++b
) {
75 uint8_t temp
= elem_i
[b
];
76 elem_i
[b
] = elem_j
[b
];
81 int elem_compare(size_t i
, const uint8_t *other
) const {
82 // An element must compare equal to itself so we don't need to consult the
83 // user provided comparator.
86 return compare
.comp_vals(get(i
), other
);
89 size_t size() const { return array_size
; }
91 // Make an Array starting at index |i| and size |s|.
92 Array
make_array(size_t i
, size_t s
) const {
93 return Array(get(i
), s
, elem_size
, compare
);
97 static size_t partition(const Array
&array
) {
98 const size_t array_size
= array
.size();
99 size_t pivot_index
= array_size
/ 2;
100 uint8_t *pivot
= array
.get(pivot_index
);
102 size_t j
= array_size
- 1;
105 int compare_i
, compare_j
;
107 while ((compare_i
= array
.elem_compare(i
, pivot
)) < 0)
109 while ((compare_j
= array
.elem_compare(j
, pivot
)) > 0)
112 // At some point i will crossover j so we will definitely break out of
119 // The pivot itself might have got swapped so we will update the pivot.
120 if (i
== pivot_index
) {
121 pivot
= array
.get(j
);
123 } else if (j
== pivot_index
) {
124 pivot
= array
.get(i
);
128 if (compare_i
== 0 && compare_j
== 0) {
129 // If we do not move the pointers, we will end up with an
130 // infinite loop as i and j will be stuck without advancing.
137 LIBC_INLINE
void quicksort(const Array
&array
) {
138 const size_t array_size
= array
.size();
141 size_t split_index
= partition(array
);
142 if (array_size
<= 2) {
143 // The partition operation sorts the two element array.
146 quicksort(array
.make_array(0, split_index
));
147 quicksort(array
.make_array(split_index
, array
.size() - split_index
));
150 } // namespace LIBC_NAMESPACE::internal
152 #endif // LLVM_LIBC_SRC_STDLIB_QSORT_UTIL_H