xref: /illumos-gate/usr/src/common/util/qsort.c (revision 44431c82)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57257d1b4Sraf  * Common Development and Distribution License (the "License").
67257d1b4Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21e8031f0aSraf 
227c478bd9Sstevel@tonic-gate /*
237257d1b4Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*44431c82SRobert Mustacchi  * Copyright 2020 Oxide Computer Company
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #if !defined(_KERNEL) && !defined(_KMDB)
297257d1b4Sraf #include "lint.h"
307c478bd9Sstevel@tonic-gate #endif /* !_KERNEL && !_KMDB */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #if !defined(_KERNEL) && !defined(_KMDB)
357c478bd9Sstevel@tonic-gate #include <stdlib.h>
367c478bd9Sstevel@tonic-gate #include <synch.h>
377c478bd9Sstevel@tonic-gate #endif /* !_KERNEL && !_KMDB */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include "qsort.h"
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate static void swapp32(uint32_t *r1, uint32_t *r2, size_t cnt);
427c478bd9Sstevel@tonic-gate static void swapp64(uint64_t *r1, uint64_t *r2, size_t cnt);
437c478bd9Sstevel@tonic-gate static void swapi(uint32_t *r1, uint32_t *r2, size_t cnt);
447c478bd9Sstevel@tonic-gate static void swapb(char *r1, char *r2, size_t cnt);
457c478bd9Sstevel@tonic-gate 
46*44431c82SRobert Mustacchi typedef int (*cmp_f)(const void *, const void *, void *);
47*44431c82SRobert Mustacchi 
487c478bd9Sstevel@tonic-gate /*
497c478bd9Sstevel@tonic-gate  * choose a median of 3 values
507c478bd9Sstevel@tonic-gate  */
51*44431c82SRobert Mustacchi static __GNU_INLINE char *
med3(char * a,char * b,char * c,cmp_f cmp,void * arg)52*44431c82SRobert Mustacchi med3(char *a, char *b, char *c, cmp_f cmp, void *arg)
53*44431c82SRobert Mustacchi {
54*44431c82SRobert Mustacchi 	if (cmp(a, b, arg) < 0) {
55*44431c82SRobert Mustacchi 		if (cmp(b, c, arg) < 0) {
56*44431c82SRobert Mustacchi 			return (b);
57*44431c82SRobert Mustacchi 		} else if (cmp(a, c, arg) < 0) {
58*44431c82SRobert Mustacchi 			return (c);
59*44431c82SRobert Mustacchi 		} else {
60*44431c82SRobert Mustacchi 			return (a);
61*44431c82SRobert Mustacchi 		}
62*44431c82SRobert Mustacchi 	} else {
63*44431c82SRobert Mustacchi 		if (cmp(b, c, arg) > 0) {
64*44431c82SRobert Mustacchi 			return (b);
65*44431c82SRobert Mustacchi 		} else if (cmp(a, c, arg) > 0) {
66*44431c82SRobert Mustacchi 			return (c);
67*44431c82SRobert Mustacchi 		} else {
68*44431c82SRobert Mustacchi 			return (a);
69*44431c82SRobert Mustacchi 		}
70*44431c82SRobert Mustacchi 	}
71*44431c82SRobert Mustacchi }
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #define	THRESH_L	5	/* threshold for insertion sort */
747c478bd9Sstevel@tonic-gate #define	THRESH_M3	20	/* threshold for median of 3 */
757c478bd9Sstevel@tonic-gate #define	THRESH_M9	50	/* threshold for median of 9 */
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate typedef struct {
787c478bd9Sstevel@tonic-gate 	char	*b_lim;
797c478bd9Sstevel@tonic-gate 	size_t	nrec;
807c478bd9Sstevel@tonic-gate } stk_t;
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate  * qsort() is a general purpose, in-place sorting routine using a
847c478bd9Sstevel@tonic-gate  * user provided call back function for comparisons.  This implementation
857c478bd9Sstevel@tonic-gate  * utilizes a ternary quicksort algorithm, and cuts over to an
867c478bd9Sstevel@tonic-gate  * insertion sort for partitions involving fewer than THRESH_L records.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  * Potential User Errors
897c478bd9Sstevel@tonic-gate  *   There is no return value from qsort, this function has no method
907c478bd9Sstevel@tonic-gate  *   of alerting the user that a sort did not work or could not work.
917c478bd9Sstevel@tonic-gate  *   We do not print an error message or exit the process or thread,
927c478bd9Sstevel@tonic-gate  *   Even if we can detect an error, We CANNOT silently return without
937c478bd9Sstevel@tonic-gate  *   sorting the data, if we did so the user could never be sure the
947c478bd9Sstevel@tonic-gate  *   sort completed successfully.
957c478bd9Sstevel@tonic-gate  *   It is possible we could change the return value of sort from void
967c478bd9Sstevel@tonic-gate  *   to int and return success or some error codes, but this gets into
977c478bd9Sstevel@tonic-gate  *   standards  and compatibility issues.
987c478bd9Sstevel@tonic-gate  *
997c478bd9Sstevel@tonic-gate  *   Examples of qsort parameter errors might be
1007c478bd9Sstevel@tonic-gate  *   1) record size (rsiz) equal to 0
1017c478bd9Sstevel@tonic-gate  *      qsort will loop and never return.
1027c478bd9Sstevel@tonic-gate  *   2) record size (rsiz) less than 0
1037c478bd9Sstevel@tonic-gate  *      rsiz is unsigned, so a negative value is insanely large
1047c478bd9Sstevel@tonic-gate  *   3) number of records (nrec) is 0
1057c478bd9Sstevel@tonic-gate  *      This is legal - qsort will return without examining any records
1067c478bd9Sstevel@tonic-gate  *   4) number of records (nrec) is less than 0
1077c478bd9Sstevel@tonic-gate  *      nrec is unsigned, so a negative value is insanely large.
1087c478bd9Sstevel@tonic-gate  *   5) nrec * rsiz > memory allocation for sort array
1097c478bd9Sstevel@tonic-gate  *      a segment violation may occur
1107c478bd9Sstevel@tonic-gate  *      corruption of other memory may occur
1117c478bd9Sstevel@tonic-gate  *   6) The base address of the sort array is invalid
1127c478bd9Sstevel@tonic-gate  *      a segment violation may occur
1137c478bd9Sstevel@tonic-gate  *      corruption of other memory may occur
1147c478bd9Sstevel@tonic-gate  *   7) The user call back function is invalid
1157c478bd9Sstevel@tonic-gate  *      we may get alignment errors or segment violations
1167c478bd9Sstevel@tonic-gate  *      we may jump into never-never land
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  *   Some less obvious errors might be
1197c478bd9Sstevel@tonic-gate  *   8) The user compare function is not comparing correctly
1207c478bd9Sstevel@tonic-gate  *   9) The user compare function modifies the data records
1217c478bd9Sstevel@tonic-gate  */
1227c478bd9Sstevel@tonic-gate 
123*44431c82SRobert Mustacchi static int
qsort_r_wrapper(const void * a,const void * b,void * arg)124*44431c82SRobert Mustacchi qsort_r_wrapper(const void *a, const void *b, void *arg)
125*44431c82SRobert Mustacchi {
126*44431c82SRobert Mustacchi 	int (*cmp)(const void *, const void *) =
127*44431c82SRobert Mustacchi 	    (int(*)(const void *, const void *))(uintptr_t)arg;
128*44431c82SRobert Mustacchi 	return (cmp(a, b));
129*44431c82SRobert Mustacchi }
130*44431c82SRobert Mustacchi 
1317c478bd9Sstevel@tonic-gate void
qsort_r(void * basep,size_t nrec,size_t rsiz,cmp_f cmp,void * arg)132*44431c82SRobert Mustacchi qsort_r(void *basep, size_t nrec, size_t rsiz,
133*44431c82SRobert Mustacchi     cmp_f cmp, void *arg)
1347c478bd9Sstevel@tonic-gate {
1357c478bd9Sstevel@tonic-gate 	size_t		i;		/* temporary variable */
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	/* variables used by swap */
1387c478bd9Sstevel@tonic-gate 	void		(*swapf)(char *, char *, size_t);
1397c478bd9Sstevel@tonic-gate 	size_t		loops;
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	/* variables used by sort */
1427c478bd9Sstevel@tonic-gate 	stk_t		stack[8 * sizeof (nrec) + 1];
1437c478bd9Sstevel@tonic-gate 	stk_t		*sp;
1447c478bd9Sstevel@tonic-gate 	char		*b_lim;		/* bottom limit */
1457c478bd9Sstevel@tonic-gate 	char		*b_dup;		/* bottom duplicate */
1467c478bd9Sstevel@tonic-gate 	char		*b_par;		/* bottom partition */
1477c478bd9Sstevel@tonic-gate 	char		*t_lim;		/* top limit */
1487c478bd9Sstevel@tonic-gate 	char		*t_dup;		/* top duplicate */
1497c478bd9Sstevel@tonic-gate 	char		*t_par;		/* top partition */
1507c478bd9Sstevel@tonic-gate 	char		*m1, *m2, *m3;	/* median pointers */
1517c478bd9Sstevel@tonic-gate 	uintptr_t	d_bytelength;	/* byte length of duplicate records */
1527c478bd9Sstevel@tonic-gate 	int		b_nrec;
1537c478bd9Sstevel@tonic-gate 	int		t_nrec;
1547c478bd9Sstevel@tonic-gate 	int		cv;		/* results of compare (bottom / top) */
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	/*
1577c478bd9Sstevel@tonic-gate 	 * choose a swap function based on alignment and size
1587c478bd9Sstevel@tonic-gate 	 *
1597c478bd9Sstevel@tonic-gate 	 * The qsort function sorts an array of fixed length records.
1607c478bd9Sstevel@tonic-gate 	 * We have very limited knowledge about the data record itself.
1617c478bd9Sstevel@tonic-gate 	 * It may be that the data record is in the array we are sorting
1627c478bd9Sstevel@tonic-gate 	 * or it may be that the array contains pointers or indexes to
1637c478bd9Sstevel@tonic-gate 	 * the actual data record and all that we are sorting is the indexes.
1647c478bd9Sstevel@tonic-gate 	 *
1657c478bd9Sstevel@tonic-gate 	 * The following decision will choose an optimal swap function
1667c478bd9Sstevel@tonic-gate 	 * based on the size and alignment of the data records
1677c478bd9Sstevel@tonic-gate 	 *   swapp64	will swap 64 bit pointers
1687c478bd9Sstevel@tonic-gate 	 *   swapp32	will swap 32 bit pointers
1697c478bd9Sstevel@tonic-gate 	 *   swapi	will swap an array of 32 bit integers
1707c478bd9Sstevel@tonic-gate 	 *   swapb	will swap an array of 8 bit characters
1717c478bd9Sstevel@tonic-gate 	 *
1727c478bd9Sstevel@tonic-gate 	 * swapi and swapb will also require the variable loops to be set
1737c478bd9Sstevel@tonic-gate 	 * to control the length of the array being swapped
1747c478bd9Sstevel@tonic-gate 	 */
1757c478bd9Sstevel@tonic-gate 	if ((((uintptr_t)basep & (sizeof (uint64_t) - 1)) == 0) &&
1767c478bd9Sstevel@tonic-gate 	    (rsiz == sizeof (uint64_t))) {
1777c478bd9Sstevel@tonic-gate 		loops = 1;
1787c478bd9Sstevel@tonic-gate 		swapf = (void (*)(char *, char *, size_t))swapp64;
1797c478bd9Sstevel@tonic-gate 	} else if ((((uintptr_t)basep & (sizeof (uint32_t) - 1)) == 0) &&
1807c478bd9Sstevel@tonic-gate 	    (rsiz == sizeof (uint32_t))) {
1817c478bd9Sstevel@tonic-gate 		loops = 1;
1827c478bd9Sstevel@tonic-gate 		swapf = (void (*)(char *, char *, size_t))swapp32;
1837c478bd9Sstevel@tonic-gate 	} else if ((((uintptr_t)basep & (sizeof (uint32_t) - 1)) == 0) &&
1847c478bd9Sstevel@tonic-gate 	    ((rsiz & (sizeof (uint32_t) - 1)) == 0)) {
1857c478bd9Sstevel@tonic-gate 		loops = rsiz / sizeof (int);
1867c478bd9Sstevel@tonic-gate 		swapf = (void (*)(char *, char *, size_t))swapi;
1877c478bd9Sstevel@tonic-gate 	} else {
1887c478bd9Sstevel@tonic-gate 		loops = rsiz;
1897c478bd9Sstevel@tonic-gate 		swapf = swapb;
1907c478bd9Sstevel@tonic-gate 	}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/*
1937c478bd9Sstevel@tonic-gate 	 * qsort is a partitioning sort
1947c478bd9Sstevel@tonic-gate 	 *
1957c478bd9Sstevel@tonic-gate 	 * the stack is the bookkeeping mechanism to keep track of all
1967c478bd9Sstevel@tonic-gate 	 * the partitions.
1977c478bd9Sstevel@tonic-gate 	 *
1987c478bd9Sstevel@tonic-gate 	 * each sort pass takes one partition and sorts it into two partitions.
1997c478bd9Sstevel@tonic-gate 	 * at the top of the loop we simply take the partition on the top
2007c478bd9Sstevel@tonic-gate 	 * of the stack and sort it. See the comments at the bottom
2017c478bd9Sstevel@tonic-gate 	 * of the loop regarding which partitions to add in what order.
2027c478bd9Sstevel@tonic-gate 	 *
2037c478bd9Sstevel@tonic-gate 	 * initially put the whole partition on the stack
2047c478bd9Sstevel@tonic-gate 	 */
2057c478bd9Sstevel@tonic-gate 	sp = stack;
2067c478bd9Sstevel@tonic-gate 	sp->b_lim = (char *)basep;
2077c478bd9Sstevel@tonic-gate 	sp->nrec = nrec;
2087c478bd9Sstevel@tonic-gate 	sp++;
2097c478bd9Sstevel@tonic-gate 	while (sp > stack) {
2107c478bd9Sstevel@tonic-gate 		sp--;
2117c478bd9Sstevel@tonic-gate 		b_lim = sp->b_lim;
2127c478bd9Sstevel@tonic-gate 		nrec = sp->nrec;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 		/*
2157c478bd9Sstevel@tonic-gate 		 * a linear insertion sort i faster than a qsort for
2167c478bd9Sstevel@tonic-gate 		 * very small number of records (THRESH_L)
2177c478bd9Sstevel@tonic-gate 		 *
2187c478bd9Sstevel@tonic-gate 		 * if number records < threshold use linear insertion sort
2197c478bd9Sstevel@tonic-gate 		 *
2207c478bd9Sstevel@tonic-gate 		 * this also handles the special case where the partition
2217c478bd9Sstevel@tonic-gate 		 * 0 or 1 records length.
2227c478bd9Sstevel@tonic-gate 		 */
2237c478bd9Sstevel@tonic-gate 		if (nrec < THRESH_L) {
2247c478bd9Sstevel@tonic-gate 			/*
2257c478bd9Sstevel@tonic-gate 			 * Linear insertion sort
2267c478bd9Sstevel@tonic-gate 			 */
2277c478bd9Sstevel@tonic-gate 			t_par = b_lim;
2287c478bd9Sstevel@tonic-gate 			for (i = 1; i < nrec; i++) {
2297c478bd9Sstevel@tonic-gate 				t_par += rsiz;
2307c478bd9Sstevel@tonic-gate 				b_par = t_par;
2317c478bd9Sstevel@tonic-gate 				while (b_par > b_lim) {
2327c478bd9Sstevel@tonic-gate 					b_par -= rsiz;
233*44431c82SRobert Mustacchi 					if ((*cmp)(b_par, b_par + rsiz, arg) <=
234*44431c82SRobert Mustacchi 					    0) {
2357c478bd9Sstevel@tonic-gate 						break;
2367c478bd9Sstevel@tonic-gate 					}
2377c478bd9Sstevel@tonic-gate 					(*swapf)(b_par, b_par + rsiz, loops);
2387c478bd9Sstevel@tonic-gate 				}
2397c478bd9Sstevel@tonic-gate 			}
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 			/*
2427c478bd9Sstevel@tonic-gate 			 * a linear insertion sort will put all records
2437c478bd9Sstevel@tonic-gate 			 * in their final position and will not create
2447c478bd9Sstevel@tonic-gate 			 * subpartitions.
2457c478bd9Sstevel@tonic-gate 			 *
2467c478bd9Sstevel@tonic-gate 			 * therefore when the insertion sort is complete
2477c478bd9Sstevel@tonic-gate 			 * just go to the top of the loop and get the
2487c478bd9Sstevel@tonic-gate 			 * next partition to sort.
2497c478bd9Sstevel@tonic-gate 			 */
2507c478bd9Sstevel@tonic-gate 			continue;
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 		/* quicksort */
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		/*
2567c478bd9Sstevel@tonic-gate 		 * choose a pivot record
2577c478bd9Sstevel@tonic-gate 		 *
2587c478bd9Sstevel@tonic-gate 		 * Ideally the pivot record will divide the partition
2597c478bd9Sstevel@tonic-gate 		 * into two equal parts. however we have to balance the
2607c478bd9Sstevel@tonic-gate 		 * work involved in selecting the pivot record with the
2617c478bd9Sstevel@tonic-gate 		 * expected benefit.
2627c478bd9Sstevel@tonic-gate 		 *
2637c478bd9Sstevel@tonic-gate 		 * The choice of pivot record depends on the number of
2647c478bd9Sstevel@tonic-gate 		 * records in the partition
2657c478bd9Sstevel@tonic-gate 		 *
2667c478bd9Sstevel@tonic-gate 		 * for small partitions (nrec < THRESH_M3)
2677c478bd9Sstevel@tonic-gate 		 *   we just select the record in the middle of the partition
2687c478bd9Sstevel@tonic-gate 		 *
2697c478bd9Sstevel@tonic-gate 		 * if (nrec >= THRESH_M3 && nrec < THRESH_M9)
2707c478bd9Sstevel@tonic-gate 		 *   we select three values and choose the median of 3
2717c478bd9Sstevel@tonic-gate 		 *
2727c478bd9Sstevel@tonic-gate 		 * if (nrec >= THRESH_M9)
2737c478bd9Sstevel@tonic-gate 		 *   then we use an approximate median of 9
2747c478bd9Sstevel@tonic-gate 		 *   9 records are selected and grouped in 3 groups of 3
2757c478bd9Sstevel@tonic-gate 		 *   the median of each of these 3 groups is fed into another
2767c478bd9Sstevel@tonic-gate 		 *   median of 3 decision.
2777c478bd9Sstevel@tonic-gate 		 *
2787c478bd9Sstevel@tonic-gate 		 * Each median of 3 decision is 2 or 3 compares,
2797c478bd9Sstevel@tonic-gate 		 * so median of 9 costs between 8 and 12 compares.
2807c478bd9Sstevel@tonic-gate 		 *
2817c478bd9Sstevel@tonic-gate 		 * i is byte distance between two consecutive samples
2827c478bd9Sstevel@tonic-gate 		 * m2 will point to the pivot record
2837c478bd9Sstevel@tonic-gate 		 */
2847c478bd9Sstevel@tonic-gate 		if (nrec < THRESH_M3) {
2857c478bd9Sstevel@tonic-gate 			m2 = b_lim + (nrec / 2) * rsiz;
2867c478bd9Sstevel@tonic-gate 		} else if (nrec < THRESH_M9) {
2877c478bd9Sstevel@tonic-gate 			/* use median of 3 */
2887c478bd9Sstevel@tonic-gate 			i = ((nrec - 1) / 2) * rsiz;
289*44431c82SRobert Mustacchi 			m2 = med3(b_lim, b_lim + i, b_lim + 2 * i, cmp, arg);
2907c478bd9Sstevel@tonic-gate 		} else {
2917c478bd9Sstevel@tonic-gate 			/* approx median of 9 */
2927c478bd9Sstevel@tonic-gate 			i = ((nrec - 1) / 8) * rsiz;
293*44431c82SRobert Mustacchi 			m1 = med3(b_lim, b_lim +  i, b_lim + 2 * i, cmp, arg);
294*44431c82SRobert Mustacchi 			m2 = med3(b_lim + 3 * i, b_lim + 4 * i, b_lim + 5 * i,
295*44431c82SRobert Mustacchi 			    cmp, arg);
296*44431c82SRobert Mustacchi 			m3 = med3(b_lim + 6 * i, b_lim + 7 * i, b_lim + 8 * i,
297*44431c82SRobert Mustacchi 			    cmp, arg);
298*44431c82SRobert Mustacchi 			m2 = med3(m1, m2, m3, cmp, arg);
2997c478bd9Sstevel@tonic-gate 		}
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 		/*
3027c478bd9Sstevel@tonic-gate 		 * quick sort partitioning
3037c478bd9Sstevel@tonic-gate 		 *
3047c478bd9Sstevel@tonic-gate 		 * The partition limits are defined by bottom and top pointers
3057c478bd9Sstevel@tonic-gate 		 * b_lim and t_lim.
3067c478bd9Sstevel@tonic-gate 		 *
3077c478bd9Sstevel@tonic-gate 		 * qsort uses a fairly standard method of moving the
3087c478bd9Sstevel@tonic-gate 		 * partitioning pointers, b_par and t_par, to the middle of
3097c478bd9Sstevel@tonic-gate 		 * the partition and exchanging records that are in the
3107c478bd9Sstevel@tonic-gate 		 * wrong part of the partition.
3117c478bd9Sstevel@tonic-gate 		 *
3127c478bd9Sstevel@tonic-gate 		 * Two enhancements have been made to the basic algorithm.
3137c478bd9Sstevel@tonic-gate 		 * One for handling duplicate records and one to minimize
3147c478bd9Sstevel@tonic-gate 		 * the number of swaps.
3157c478bd9Sstevel@tonic-gate 		 *
3167c478bd9Sstevel@tonic-gate 		 * Two duplicate records pointers are (b_dup and t_dup) are
3177c478bd9Sstevel@tonic-gate 		 * initially set to b_lim and t_lim.  Each time a record
3187c478bd9Sstevel@tonic-gate 		 * whose sort key value is equal to the pivot record is found
3197c478bd9Sstevel@tonic-gate 		 * it will be swapped with the record pointed to by
3207c478bd9Sstevel@tonic-gate 		 * b_dup or t_dup and the duplicate pointer will be
3217c478bd9Sstevel@tonic-gate 		 * incremented toward the center.
3227c478bd9Sstevel@tonic-gate 		 * When partitioning is complete, all the duplicate records
3237c478bd9Sstevel@tonic-gate 		 * will have been collected at the upper and lower limits of
3247c478bd9Sstevel@tonic-gate 		 * the partition and can easily be moved adjacent to the
3257c478bd9Sstevel@tonic-gate 		 * pivot record.
3267c478bd9Sstevel@tonic-gate 		 *
3277c478bd9Sstevel@tonic-gate 		 * The second optimization is to minimize the number of swaps.
3287c478bd9Sstevel@tonic-gate 		 * The pointer m2 points to the pivot record.
3297c478bd9Sstevel@tonic-gate 		 * During partitioning, if m2 is ever equal to the partitioning
3307c478bd9Sstevel@tonic-gate 		 * pointers, b_par or t_par, then b_par or t_par just moves
3317c478bd9Sstevel@tonic-gate 		 * onto the next record without doing a compare.
3327c478bd9Sstevel@tonic-gate 		 * If as a result of duplicate record detection,
3337c478bd9Sstevel@tonic-gate 		 * b_dup or t_dup is ever equal to m2,
3347c478bd9Sstevel@tonic-gate 		 * then m2 is changed to point to the duplicate record and
3357c478bd9Sstevel@tonic-gate 		 * b_dup or t_dup is incremented with out swapping records.
3367c478bd9Sstevel@tonic-gate 		 *
3377c478bd9Sstevel@tonic-gate 		 * When partitioning is done, we may not have the same pivot
3387c478bd9Sstevel@tonic-gate 		 * record that we started with, but we will have one with
3397c478bd9Sstevel@tonic-gate 		 * an equal sort key.
3407c478bd9Sstevel@tonic-gate 		 */
3417c478bd9Sstevel@tonic-gate 		b_dup = b_par		= b_lim;
3427c478bd9Sstevel@tonic-gate 		t_dup = t_par = t_lim	= b_lim + rsiz * (nrec - 1);
3437c478bd9Sstevel@tonic-gate 		for (;;) {
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 			/* move bottom pointer up */
3467c478bd9Sstevel@tonic-gate 			for (; b_par <= t_par; b_par += rsiz) {
3477c478bd9Sstevel@tonic-gate 				if (b_par == m2) {
3487c478bd9Sstevel@tonic-gate 					continue;
3497c478bd9Sstevel@tonic-gate 				}
350*44431c82SRobert Mustacchi 				cv = cmp(b_par, m2, arg);
3517c478bd9Sstevel@tonic-gate 				if (cv > 0) {
3527c478bd9Sstevel@tonic-gate 					break;
3537c478bd9Sstevel@tonic-gate 				}
3547c478bd9Sstevel@tonic-gate 				if (cv == 0) {
3557c478bd9Sstevel@tonic-gate 					if (b_dup == m2) {
3567c478bd9Sstevel@tonic-gate 						m2 = b_par;
3577c478bd9Sstevel@tonic-gate 					} else if (b_dup != b_par) {
3587c478bd9Sstevel@tonic-gate 						(*swapf)(b_dup, b_par, loops);
3597c478bd9Sstevel@tonic-gate 					}
3607c478bd9Sstevel@tonic-gate 					b_dup += rsiz;
3617c478bd9Sstevel@tonic-gate 				}
3627c478bd9Sstevel@tonic-gate 			}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 			/* move top pointer down */
3657c478bd9Sstevel@tonic-gate 			for (; b_par < t_par; t_par -= rsiz) {
3667c478bd9Sstevel@tonic-gate 				if (t_par == m2) {
3677c478bd9Sstevel@tonic-gate 					continue;
3687c478bd9Sstevel@tonic-gate 				}
369*44431c82SRobert Mustacchi 				cv = cmp(t_par, m2, arg);
3707c478bd9Sstevel@tonic-gate 				if (cv < 0) {
3717c478bd9Sstevel@tonic-gate 					break;
3727c478bd9Sstevel@tonic-gate 				}
3737c478bd9Sstevel@tonic-gate 				if (cv == 0) {
3747c478bd9Sstevel@tonic-gate 					if (t_dup == m2) {
3757c478bd9Sstevel@tonic-gate 						m2 = t_par;
3767c478bd9Sstevel@tonic-gate 					} else if (t_dup != t_par) {
3777c478bd9Sstevel@tonic-gate 						(*swapf)(t_dup, t_par, loops);
3787c478bd9Sstevel@tonic-gate 					}
3797c478bd9Sstevel@tonic-gate 					t_dup -= rsiz;
3807c478bd9Sstevel@tonic-gate 				}
3817c478bd9Sstevel@tonic-gate 			}
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 			/* break if we are done partitioning */
3847c478bd9Sstevel@tonic-gate 			if (b_par >= t_par) {
3857c478bd9Sstevel@tonic-gate 				break;
3867c478bd9Sstevel@tonic-gate 			}
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 			/* exchange records at upper and lower break points */
3897c478bd9Sstevel@tonic-gate 			(*swapf)(b_par, t_par, loops);
3907c478bd9Sstevel@tonic-gate 			b_par += rsiz;
3917c478bd9Sstevel@tonic-gate 			t_par -= rsiz;
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 		/*
3957c478bd9Sstevel@tonic-gate 		 * partitioning is now complete
3967c478bd9Sstevel@tonic-gate 		 *
3977c478bd9Sstevel@tonic-gate 		 * there are two termination conditions from the partitioning
3987c478bd9Sstevel@tonic-gate 		 * loop above.  Either b_par or t_par have crossed or
3997c478bd9Sstevel@tonic-gate 		 * they are equal.
4007c478bd9Sstevel@tonic-gate 		 *
4017c478bd9Sstevel@tonic-gate 		 * we need to swap the pivot record to its final position
4027c478bd9Sstevel@tonic-gate 		 * m2 could be in either the upper or lower partitions
4037c478bd9Sstevel@tonic-gate 		 * or it could already be in its final position
4047c478bd9Sstevel@tonic-gate 		 */
4057c478bd9Sstevel@tonic-gate 		/*
4067c478bd9Sstevel@tonic-gate 		 * R[b_par] > R[m2]
4077c478bd9Sstevel@tonic-gate 		 * R[t_par] < R[m2]
4087c478bd9Sstevel@tonic-gate 		 */
4097c478bd9Sstevel@tonic-gate 		if (t_par < b_par) {
4107c478bd9Sstevel@tonic-gate 			if (m2 < t_par) {
4117c478bd9Sstevel@tonic-gate 				(*swapf)(m2, t_par, loops);
4127c478bd9Sstevel@tonic-gate 				m2 = b_par = t_par;
4137c478bd9Sstevel@tonic-gate 			} else if (m2 > b_par) {
4147c478bd9Sstevel@tonic-gate 				(*swapf)(m2, b_par, loops);
4157c478bd9Sstevel@tonic-gate 				m2 = t_par = b_par;
4167c478bd9Sstevel@tonic-gate 			} else {
4177c478bd9Sstevel@tonic-gate 				b_par = t_par = m2;
4187c478bd9Sstevel@tonic-gate 			}
4197c478bd9Sstevel@tonic-gate 		} else {
4207c478bd9Sstevel@tonic-gate 			if (m2 < t_par) {
4217c478bd9Sstevel@tonic-gate 				t_par = b_par = t_par - rsiz;
4227c478bd9Sstevel@tonic-gate 			}
4237c478bd9Sstevel@tonic-gate 			if (m2 != b_par) {
4247c478bd9Sstevel@tonic-gate 				(*swapf)(m2, b_par, loops);
4257c478bd9Sstevel@tonic-gate 			}
4267c478bd9Sstevel@tonic-gate 			m2 = t_par;
4277c478bd9Sstevel@tonic-gate 		}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 		/*
4307c478bd9Sstevel@tonic-gate 		 * move bottom duplicates next to pivot
4317c478bd9Sstevel@tonic-gate 		 * optimized to eliminate overlap
4327c478bd9Sstevel@tonic-gate 		 */
4337c478bd9Sstevel@tonic-gate 		d_bytelength = b_dup - b_lim;
4347c478bd9Sstevel@tonic-gate 		if (b_par - b_dup < d_bytelength) {
4357c478bd9Sstevel@tonic-gate 			b_dup = b_lim + (b_par - b_dup);
4367c478bd9Sstevel@tonic-gate 		}
4377c478bd9Sstevel@tonic-gate 		while (b_dup > b_lim) {
4387c478bd9Sstevel@tonic-gate 			b_dup -= rsiz;
4397c478bd9Sstevel@tonic-gate 			b_par -= rsiz;
4407c478bd9Sstevel@tonic-gate 			(*swapf)(b_dup, b_par, loops);
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 		b_par = m2 - d_bytelength;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 		/*
4457c478bd9Sstevel@tonic-gate 		 * move top duplicates next to pivot
4467c478bd9Sstevel@tonic-gate 		 */
4477c478bd9Sstevel@tonic-gate 		d_bytelength = t_lim - t_dup;
4487c478bd9Sstevel@tonic-gate 		if (t_dup - t_par < d_bytelength) {
4497c478bd9Sstevel@tonic-gate 			t_dup = t_lim - (t_dup - t_par);
4507c478bd9Sstevel@tonic-gate 		}
4517c478bd9Sstevel@tonic-gate 		while (t_dup < t_lim) {
4527c478bd9Sstevel@tonic-gate 			t_dup += rsiz;
4537c478bd9Sstevel@tonic-gate 			t_par += rsiz;
4547c478bd9Sstevel@tonic-gate 			(*swapf)(t_dup, t_par, loops);
4557c478bd9Sstevel@tonic-gate 		}
4567c478bd9Sstevel@tonic-gate 		t_par = m2 + d_bytelength;
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 		/*
4597c478bd9Sstevel@tonic-gate 		 * when a qsort pass completes there are three partitions
4607c478bd9Sstevel@tonic-gate 		 * 1) the lower contains all records less than pivot
4617c478bd9Sstevel@tonic-gate 		 * 2) the upper contains all records greater than pivot
4627c478bd9Sstevel@tonic-gate 		 * 3) the pivot partition contains all record equal to pivot
4637c478bd9Sstevel@tonic-gate 		 *
4647c478bd9Sstevel@tonic-gate 		 * all records in the pivot partition are in their final
4657c478bd9Sstevel@tonic-gate 		 * position and do not need to be accounted for by the stack
4667c478bd9Sstevel@tonic-gate 		 *
4677c478bd9Sstevel@tonic-gate 		 * when adding partitions to the stack
4687c478bd9Sstevel@tonic-gate 		 * it is important to add the largest partition first
4697c478bd9Sstevel@tonic-gate 		 * to prevent stack overflow.
4707c478bd9Sstevel@tonic-gate 		 *
4717c478bd9Sstevel@tonic-gate 		 * calculate number of unsorted records in top and bottom
4727c478bd9Sstevel@tonic-gate 		 * push resulting partitions on stack
4737c478bd9Sstevel@tonic-gate 		 */
4747c478bd9Sstevel@tonic-gate 		b_nrec = (b_par - b_lim) / rsiz;
4757c478bd9Sstevel@tonic-gate 		t_nrec = (t_lim - t_par) / rsiz;
4767c478bd9Sstevel@tonic-gate 		if (b_nrec < t_nrec) {
4777c478bd9Sstevel@tonic-gate 			sp->b_lim = t_par + rsiz;
4787c478bd9Sstevel@tonic-gate 			sp->nrec = t_nrec;
4797c478bd9Sstevel@tonic-gate 			sp++;
4807c478bd9Sstevel@tonic-gate 			sp->b_lim = b_lim;
4817c478bd9Sstevel@tonic-gate 			sp->nrec = b_nrec;
4827c478bd9Sstevel@tonic-gate 			sp++;
4837c478bd9Sstevel@tonic-gate 		} else {
4847c478bd9Sstevel@tonic-gate 			sp->b_lim = b_lim;
4857c478bd9Sstevel@tonic-gate 			sp->nrec = b_nrec;
4867c478bd9Sstevel@tonic-gate 			sp++;
4877c478bd9Sstevel@tonic-gate 			sp->b_lim = t_par + rsiz;
4887c478bd9Sstevel@tonic-gate 			sp->nrec = t_nrec;
4897c478bd9Sstevel@tonic-gate 			sp++;
4907c478bd9Sstevel@tonic-gate 		}
4917c478bd9Sstevel@tonic-gate 	}
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate 
494*44431c82SRobert Mustacchi void
qsort(void * basep,size_t nrec,size_t rsiz,int (* cmp)(const void *,const void *))495*44431c82SRobert Mustacchi qsort(void *basep, size_t nrec, size_t rsiz,
496*44431c82SRobert Mustacchi     int (*cmp)(const void *, const void *))
497*44431c82SRobert Mustacchi {
498*44431c82SRobert Mustacchi 	return (qsort_r(basep, nrec, rsiz, qsort_r_wrapper,
499*44431c82SRobert Mustacchi 	    (void *)(uintptr_t)cmp));
500*44431c82SRobert Mustacchi }
501*44431c82SRobert Mustacchi 
5027c478bd9Sstevel@tonic-gate /*
5037c478bd9Sstevel@tonic-gate  * The following swap functions should not create a stack frame
5047c478bd9Sstevel@tonic-gate  * the SPARC call / return instruction will be executed
5057c478bd9Sstevel@tonic-gate  * but the a save / restore will not be executed
5067c478bd9Sstevel@tonic-gate  * which means we won't do a window turn with the spill / fill overhead
5077c478bd9Sstevel@tonic-gate  * verify this by examining the assembly code
5087c478bd9Sstevel@tonic-gate  */
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate /* ARGSUSED */
5117c478bd9Sstevel@tonic-gate static void
swapp32(uint32_t * r1,uint32_t * r2,size_t cnt)5127c478bd9Sstevel@tonic-gate swapp32(uint32_t *r1, uint32_t *r2, size_t cnt)
5137c478bd9Sstevel@tonic-gate {
5147c478bd9Sstevel@tonic-gate 	uint32_t temp;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	temp = *r1;
5177c478bd9Sstevel@tonic-gate 	*r1++ = *r2;
5187c478bd9Sstevel@tonic-gate 	*r2++ = temp;
5197c478bd9Sstevel@tonic-gate }
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate /* ARGSUSED */
5227c478bd9Sstevel@tonic-gate static void
swapp64(uint64_t * r1,uint64_t * r2,size_t cnt)5237c478bd9Sstevel@tonic-gate swapp64(uint64_t *r1, uint64_t *r2, size_t cnt)
5247c478bd9Sstevel@tonic-gate {
5257c478bd9Sstevel@tonic-gate 	uint64_t temp;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	temp = *r1;
5287c478bd9Sstevel@tonic-gate 	*r1++ = *r2;
5297c478bd9Sstevel@tonic-gate 	*r2++ = temp;
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate static void
swapi(uint32_t * r1,uint32_t * r2,size_t cnt)5337c478bd9Sstevel@tonic-gate swapi(uint32_t *r1, uint32_t *r2, size_t cnt)
5347c478bd9Sstevel@tonic-gate {
5357c478bd9Sstevel@tonic-gate 	uint32_t temp;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/* character by character */
5387c478bd9Sstevel@tonic-gate 	while (cnt--) {
5397c478bd9Sstevel@tonic-gate 		temp = *r1;
5407c478bd9Sstevel@tonic-gate 		*r1++ = *r2;
5417c478bd9Sstevel@tonic-gate 		*r2++ = temp;
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate }
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate static void
swapb(char * r1,char * r2,size_t cnt)5467c478bd9Sstevel@tonic-gate swapb(char *r1, char *r2, size_t cnt)
5477c478bd9Sstevel@tonic-gate {
5487c478bd9Sstevel@tonic-gate 	char	temp;
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	/* character by character */
5517c478bd9Sstevel@tonic-gate 	while (cnt--) {
5527c478bd9Sstevel@tonic-gate 		temp = *r1;
5537c478bd9Sstevel@tonic-gate 		*r1++ = *r2;
5547c478bd9Sstevel@tonic-gate 		*r2++ = temp;
5557c478bd9Sstevel@tonic-gate 	}
5567c478bd9Sstevel@tonic-gate }
557