summaryrefslogtreecommitdiff
path: root/stdlib/qsort_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/qsort_common.c')
-rw-r--r--stdlib/qsort_common.c63
1 files changed, 60 insertions, 3 deletions
diff --git a/stdlib/qsort_common.c b/stdlib/qsort_common.c
index 666b1958ab..96274f2150 100644
--- a/stdlib/qsort_common.c
+++ b/stdlib/qsort_common.c
@@ -23,15 +23,65 @@
#ifdef R_VERSION
# define R_CMP_TYPE __compar_d_fn_t
# define R_CMP_ARG , void *arg
+# define R_CMP_ARG_USE , arg
# define R_CMP(p1, p2) cmp (p1, p2, arg)
#else
# define R_CMP_TYPE __compar_fn_t
# define R_CMP_ARG
+# define R_CMP_ARG_USE
# define R_CMP(p1, p2) cmp (p1, p2)
#endif
-/* Order size using quicksort. This implementation incorporates
- four optimizations discussed in Sedgewick:
+/* A fast, small, non-recursive O(nlog n) heapsort, adapted from Linux
+ lib/sort.c. Used on introsort implementation as a fallback routine with
+ worst-case performance of O(nlog n) and worst-case space complexity of
+ O(1). */
+
+static void
+R_HEAP (void *pbase, size_t total_elems, size_t size, swap_t swap,
+ R_CMP_TYPE cmp R_CMP_ARG)
+{
+ size_t i = (total_elems / 2 - 1) * size;
+ size_t n = total_elems * size;
+ size_t r, c;
+
+ while (1)
+ {
+ for (r = i; r * 2 + size < n; r = c)
+ {
+ c = r * 2 + size;
+ if (c < n - size && R_CMP (pbase + c, pbase + c + size) < 0)
+ c += size;
+ if (R_CMP (pbase + r, pbase + c) >= 0)
+ break;
+ swap (pbase + r, pbase + c, size);
+ }
+ if (i == 0)
+ break;
+ i -= size;
+ }
+
+ i = n - size;
+ while (1)
+ {
+ swap (pbase, pbase + i, size);
+ for (r = 0; r * 2 + size < i; r = c)
+ {
+ c = r * 2 + size;
+ if (c < i - size && R_CMP (pbase + c, pbase + c + size) < 0)
+ c += size;
+ if (R_CMP (pbase + r, pbase + c) >= 0)
+ break;
+ swap (pbase + r, pbase + c, size);
+ }
+ i -= size;
+ if (i == 0)
+ break;
+ }
+}
+
+/* Introsort to avoid worst case scenario of quicksort. For quicksort
+ the implementation incorporates four optimizations discussed in Sedgewick:
1. Non-recursive, using an explicit stack of pointer that store the
next array partition to sort. To save time, this maximum amount
@@ -57,7 +107,7 @@
void
R_FUNC (void *pbase, size_t total_elems, size_t size, R_CMP_TYPE cmp R_CMP_ARG)
{
- if (total_elems == 0)
+ if (total_elems <= 1)
/* Avoid lossage with unsigned arithmetic below. */
return;
@@ -67,6 +117,8 @@ R_FUNC (void *pbase, size_t total_elems, size_t size, R_CMP_TYPE cmp R_CMP_ARG)
swap_t swap = select_swap_func (pbase, size);
+ size_t depth = 2 * lg (total_elems);
+
if (total_elems > MAX_THRESH)
{
char *lo = base_ptr;
@@ -78,6 +130,9 @@ R_FUNC (void *pbase, size_t total_elems, size_t size, R_CMP_TYPE cmp R_CMP_ARG)
while (STACK_NOT_EMPTY)
{
+ if (depth-- == 0)
+ return R_HEAP (pbase, total_elems, size, swap, cmp R_CMP_ARG_USE);
+
char *left_ptr;
char *right_ptr;
@@ -220,6 +275,8 @@ R_FUNC (void *pbase, size_t total_elems, size_t size, R_CMP_TYPE cmp R_CMP_ARG)
#undef R_NAME
#undef R_CMP_TYPE
#undef R_CMP_ARG
+#undef R_CMP_ARG_USE
#undef R_CMP
#undef R_FUNC
#undef R_VERSION
+#undef R_HEAP