xref: /dragonfly/usr.bin/sort/radixsort.c (revision 07774aea)
150fc853eSJohn Marino /*-
250fc853eSJohn Marino  * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
350fc853eSJohn Marino  * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
450fc853eSJohn Marino  * All rights reserved.
550fc853eSJohn Marino  *
650fc853eSJohn Marino  * Redistribution and use in source and binary forms, with or without
750fc853eSJohn Marino  * modification, are permitted provided that the following conditions
850fc853eSJohn Marino  * are met:
950fc853eSJohn Marino  * 1. Redistributions of source code must retain the above copyright
1050fc853eSJohn Marino  *    notice, this list of conditions and the following disclaimer.
1150fc853eSJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1250fc853eSJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1350fc853eSJohn Marino  *    documentation and/or other materials provided with the distribution.
1450fc853eSJohn Marino  *
1550fc853eSJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650fc853eSJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750fc853eSJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850fc853eSJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950fc853eSJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050fc853eSJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150fc853eSJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250fc853eSJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350fc853eSJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450fc853eSJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550fc853eSJohn Marino  * SUCH DAMAGE.
2650fc853eSJohn Marino  *
2750fc853eSJohn Marino  * $FreeBSD: head/usr.bin/sort/radixsort.c 281133 2015-04-06 03:02:20Z pfg $
2850fc853eSJohn Marino  */
2950fc853eSJohn Marino 
3050fc853eSJohn Marino 
3150fc853eSJohn Marino #include <errno.h>
3250fc853eSJohn Marino #include <err.h>
3350fc853eSJohn Marino #include <langinfo.h>
3450fc853eSJohn Marino #include <math.h>
3550fc853eSJohn Marino #if defined(SORT_THREADS)
3650fc853eSJohn Marino #include <pthread.h>
3750fc853eSJohn Marino #include <semaphore.h>
3850fc853eSJohn Marino #endif
3950fc853eSJohn Marino #include <stdlib.h>
4050fc853eSJohn Marino #include <string.h>
4150fc853eSJohn Marino #include <wchar.h>
4250fc853eSJohn Marino #include <wctype.h>
4350fc853eSJohn Marino #include <unistd.h>
4450fc853eSJohn Marino 
4550fc853eSJohn Marino #include "coll.h"
4650fc853eSJohn Marino #include "radixsort.h"
4750fc853eSJohn Marino 
4850fc853eSJohn Marino #define DEFAULT_SORT_FUNC_RADIXSORT mergesort
4950fc853eSJohn Marino 
5050fc853eSJohn Marino #define TINY_NODE(sl) ((sl)->tosort_num < 65)
5150fc853eSJohn Marino #define SMALL_NODE(sl) ((sl)->tosort_num < 5)
5250fc853eSJohn Marino 
5350fc853eSJohn Marino /* are we sorting in reverse order ? */
5450fc853eSJohn Marino static bool reverse_sort;
5550fc853eSJohn Marino 
5650fc853eSJohn Marino /* sort sub-levels array size */
5750fc853eSJohn Marino static const size_t slsz = 256 * sizeof(struct sort_level*);
5850fc853eSJohn Marino 
5950fc853eSJohn Marino /* one sort level structure */
6050fc853eSJohn Marino struct sort_level
6150fc853eSJohn Marino {
6250fc853eSJohn Marino 	struct sort_level	**sublevels;
6350fc853eSJohn Marino 	struct sort_list_item	**leaves;
6450fc853eSJohn Marino 	struct sort_list_item	**sorted;
6550fc853eSJohn Marino 	struct sort_list_item	**tosort;
6650fc853eSJohn Marino 	size_t			  leaves_num;
6750fc853eSJohn Marino 	size_t			  leaves_sz;
6850fc853eSJohn Marino 	size_t			  level;
6950fc853eSJohn Marino 	size_t			  real_sln;
7050fc853eSJohn Marino 	size_t			  start_position;
7150fc853eSJohn Marino 	size_t			  sln;
7250fc853eSJohn Marino 	size_t			  tosort_num;
7350fc853eSJohn Marino 	size_t			  tosort_sz;
7450fc853eSJohn Marino };
7550fc853eSJohn Marino 
7650fc853eSJohn Marino /* stack of sort levels ready to be sorted */
7750fc853eSJohn Marino struct level_stack {
7850fc853eSJohn Marino 	struct level_stack	 *next;
7950fc853eSJohn Marino 	struct sort_level	 *sl;
8050fc853eSJohn Marino };
8150fc853eSJohn Marino 
8250fc853eSJohn Marino static struct level_stack *g_ls;
8350fc853eSJohn Marino 
8450fc853eSJohn Marino #if defined(SORT_THREADS)
8550fc853eSJohn Marino /* stack guarding mutex */
86*07774aeaSMatthew Dillon static pthread_cond_t g_ls_cond;
8750fc853eSJohn Marino static pthread_mutex_t g_ls_mutex;
8850fc853eSJohn Marino 
8950fc853eSJohn Marino /* counter: how many items are left */
9050fc853eSJohn Marino static size_t sort_left;
9150fc853eSJohn Marino /* guarding mutex */
9250fc853eSJohn Marino 
9350fc853eSJohn Marino /* semaphore to count threads */
9450fc853eSJohn Marino static sem_t mtsem;
9550fc853eSJohn Marino 
9650fc853eSJohn Marino /*
9750fc853eSJohn Marino  * Decrement items counter
9850fc853eSJohn Marino  */
9950fc853eSJohn Marino static inline void
sort_left_dec(size_t n)10050fc853eSJohn Marino sort_left_dec(size_t n)
10150fc853eSJohn Marino {
102*07774aeaSMatthew Dillon 	pthread_mutex_lock(&g_ls_mutex);
10350fc853eSJohn Marino 	sort_left -= n;
104*07774aeaSMatthew Dillon 	if (sort_left == 0 && nthreads > 1) {
105*07774aeaSMatthew Dillon 		pthread_cond_broadcast(&g_ls_cond);
106*07774aeaSMatthew Dillon 	}
107*07774aeaSMatthew Dillon 	pthread_mutex_unlock(&g_ls_mutex);
10850fc853eSJohn Marino }
10950fc853eSJohn Marino 
11050fc853eSJohn Marino /*
11150fc853eSJohn Marino  * Do we have something to sort ?
112*07774aeaSMatthew Dillon  *
113*07774aeaSMatthew Dillon  * This routine does not need to be locked.
11450fc853eSJohn Marino  */
11550fc853eSJohn Marino static inline bool
have_sort_left(void)11650fc853eSJohn Marino have_sort_left(void)
11750fc853eSJohn Marino {
11850fc853eSJohn Marino 	bool ret;
11950fc853eSJohn Marino 
12050fc853eSJohn Marino 	ret = (sort_left > 0);
121*07774aeaSMatthew Dillon 
12250fc853eSJohn Marino 	return (ret);
12350fc853eSJohn Marino }
12450fc853eSJohn Marino 
12550fc853eSJohn Marino #else
12650fc853eSJohn Marino 
12750fc853eSJohn Marino #define sort_left_dec(n)
12850fc853eSJohn Marino 
12950fc853eSJohn Marino #endif /* SORT_THREADS */
13050fc853eSJohn Marino 
13150fc853eSJohn Marino /*
13250fc853eSJohn Marino  * Push sort level to the stack
13350fc853eSJohn Marino  */
13450fc853eSJohn Marino static inline void
push_ls(struct sort_level * sl)13550fc853eSJohn Marino push_ls(struct sort_level *sl)
13650fc853eSJohn Marino {
13750fc853eSJohn Marino 	struct level_stack *new_ls;
13850fc853eSJohn Marino 
13950fc853eSJohn Marino 	new_ls = sort_malloc(sizeof(struct level_stack));
14050fc853eSJohn Marino 	new_ls->sl = sl;
14150fc853eSJohn Marino 
14250fc853eSJohn Marino #if defined(SORT_THREADS)
14350fc853eSJohn Marino 	if (nthreads > 1)
14450fc853eSJohn Marino 		pthread_mutex_lock(&g_ls_mutex);
14550fc853eSJohn Marino #endif
14650fc853eSJohn Marino 
14750fc853eSJohn Marino 	new_ls->next = g_ls;
14850fc853eSJohn Marino 	g_ls = new_ls;
14950fc853eSJohn Marino 
15050fc853eSJohn Marino #if defined(SORT_THREADS)
151*07774aeaSMatthew Dillon 	if (nthreads > 1) {
152*07774aeaSMatthew Dillon 		pthread_cond_signal(&g_ls_cond);
153*07774aeaSMatthew Dillon 	}
154*07774aeaSMatthew Dillon #endif
155*07774aeaSMatthew Dillon 
156*07774aeaSMatthew Dillon #if defined(SORT_THREADS)
15750fc853eSJohn Marino 	if (nthreads > 1)
15850fc853eSJohn Marino 		pthread_mutex_unlock(&g_ls_mutex);
15950fc853eSJohn Marino #endif
16050fc853eSJohn Marino }
16150fc853eSJohn Marino 
16250fc853eSJohn Marino /*
16350fc853eSJohn Marino  * Pop sort level from the stack (single-threaded style)
16450fc853eSJohn Marino  */
16550fc853eSJohn Marino static inline struct sort_level*
pop_ls_st(void)16650fc853eSJohn Marino pop_ls_st(void)
16750fc853eSJohn Marino {
16850fc853eSJohn Marino 	struct sort_level *sl;
16950fc853eSJohn Marino 
17050fc853eSJohn Marino 	if (g_ls) {
17150fc853eSJohn Marino 		struct level_stack *saved_ls;
17250fc853eSJohn Marino 
17350fc853eSJohn Marino 		sl = g_ls->sl;
17450fc853eSJohn Marino 		saved_ls = g_ls;
17550fc853eSJohn Marino 		g_ls = g_ls->next;
17650fc853eSJohn Marino 		sort_free(saved_ls);
17750fc853eSJohn Marino 	} else
17850fc853eSJohn Marino 		sl = NULL;
17950fc853eSJohn Marino 
18050fc853eSJohn Marino 	return (sl);
18150fc853eSJohn Marino }
18250fc853eSJohn Marino 
18350fc853eSJohn Marino #if defined(SORT_THREADS)
18450fc853eSJohn Marino 
18550fc853eSJohn Marino /*
18650fc853eSJohn Marino  * Pop sort level from the stack (multi-threaded style)
18750fc853eSJohn Marino  */
18850fc853eSJohn Marino static inline struct sort_level*
pop_ls_mt(void)18950fc853eSJohn Marino pop_ls_mt(void)
19050fc853eSJohn Marino {
19150fc853eSJohn Marino 	struct level_stack *saved_ls;
19250fc853eSJohn Marino 	struct sort_level *sl;
19350fc853eSJohn Marino 
19450fc853eSJohn Marino 	pthread_mutex_lock(&g_ls_mutex);
19550fc853eSJohn Marino 
196*07774aeaSMatthew Dillon 	for (;;) {
19750fc853eSJohn Marino 		if (g_ls) {
19850fc853eSJohn Marino 			sl = g_ls->sl;
19950fc853eSJohn Marino 			saved_ls = g_ls;
20050fc853eSJohn Marino 			g_ls = g_ls->next;
201*07774aeaSMatthew Dillon 			break;
202*07774aeaSMatthew Dillon 		}
20350fc853eSJohn Marino 		sl = NULL;
20450fc853eSJohn Marino 		saved_ls = NULL;
205*07774aeaSMatthew Dillon 
206*07774aeaSMatthew Dillon 		if (have_sort_left() == 0)
207*07774aeaSMatthew Dillon 			break;
208*07774aeaSMatthew Dillon 		pthread_cond_wait(&g_ls_cond, &g_ls_mutex);
20950fc853eSJohn Marino 	}
21050fc853eSJohn Marino 
21150fc853eSJohn Marino 	pthread_mutex_unlock(&g_ls_mutex);
21250fc853eSJohn Marino 
21350fc853eSJohn Marino 	sort_free(saved_ls);
21450fc853eSJohn Marino 
21550fc853eSJohn Marino 	return (sl);
21650fc853eSJohn Marino }
21750fc853eSJohn Marino 
21850fc853eSJohn Marino #endif /* defined(SORT_THREADS) */
21950fc853eSJohn Marino 
22050fc853eSJohn Marino static void
add_to_sublevel(struct sort_level * sl,struct sort_list_item * item,size_t indx)22150fc853eSJohn Marino add_to_sublevel(struct sort_level *sl, struct sort_list_item *item, size_t indx)
22250fc853eSJohn Marino {
22350fc853eSJohn Marino 	struct sort_level *ssl;
22450fc853eSJohn Marino 
22550fc853eSJohn Marino 	ssl = sl->sublevels[indx];
22650fc853eSJohn Marino 
22750fc853eSJohn Marino 	if (ssl == NULL) {
22850fc853eSJohn Marino 		ssl = sort_malloc(sizeof(struct sort_level));
22950fc853eSJohn Marino 		memset(ssl, 0, sizeof(struct sort_level));
23050fc853eSJohn Marino 
23150fc853eSJohn Marino 		ssl->level = sl->level + 1;
23250fc853eSJohn Marino 		sl->sublevels[indx] = ssl;
23350fc853eSJohn Marino 
23450fc853eSJohn Marino 		++(sl->real_sln);
23550fc853eSJohn Marino 	}
23650fc853eSJohn Marino 
23750fc853eSJohn Marino 	if (++(ssl->tosort_num) > ssl->tosort_sz) {
23850fc853eSJohn Marino 		ssl->tosort_sz = ssl->tosort_num + 128;
23950fc853eSJohn Marino 		ssl->tosort = sort_realloc(ssl->tosort,
24050fc853eSJohn Marino 		    sizeof(struct sort_list_item*) * (ssl->tosort_sz));
24150fc853eSJohn Marino 	}
24250fc853eSJohn Marino 
24350fc853eSJohn Marino 	ssl->tosort[ssl->tosort_num - 1] = item;
24450fc853eSJohn Marino }
24550fc853eSJohn Marino 
24650fc853eSJohn Marino static inline void
add_leaf(struct sort_level * sl,struct sort_list_item * item)24750fc853eSJohn Marino add_leaf(struct sort_level *sl, struct sort_list_item *item)
24850fc853eSJohn Marino {
24950fc853eSJohn Marino 
25050fc853eSJohn Marino 	if (++(sl->leaves_num) > sl->leaves_sz) {
25150fc853eSJohn Marino 		sl->leaves_sz = sl->leaves_num + 128;
25250fc853eSJohn Marino 		sl->leaves = sort_realloc(sl->leaves,
25350fc853eSJohn Marino 		    (sizeof(struct sort_list_item*) * (sl->leaves_sz)));
25450fc853eSJohn Marino 	}
25550fc853eSJohn Marino 	sl->leaves[sl->leaves_num - 1] = item;
25650fc853eSJohn Marino }
25750fc853eSJohn Marino 
25850fc853eSJohn Marino static inline int
get_wc_index(struct sort_list_item * sli,size_t level)25950fc853eSJohn Marino get_wc_index(struct sort_list_item *sli, size_t level)
26050fc853eSJohn Marino {
26150fc853eSJohn Marino 	const struct bwstring *bws;
26250fc853eSJohn Marino 
26350fc853eSJohn Marino 	bws = sli->ka.key[0].k;
26450fc853eSJohn Marino 
26550fc853eSJohn Marino 	if ((BWSLEN(bws) > level))
26650fc853eSJohn Marino 		return (unsigned char) BWS_GET(bws,level);
26750fc853eSJohn Marino 	return (-1);
26850fc853eSJohn Marino }
26950fc853eSJohn Marino 
27050fc853eSJohn Marino static void
place_item(struct sort_level * sl,size_t item)27150fc853eSJohn Marino place_item(struct sort_level *sl, size_t item)
27250fc853eSJohn Marino {
27350fc853eSJohn Marino 	struct sort_list_item *sli;
27450fc853eSJohn Marino 	int c;
27550fc853eSJohn Marino 
27650fc853eSJohn Marino 	sli = sl->tosort[item];
27750fc853eSJohn Marino 	c = get_wc_index(sli, sl->level);
27850fc853eSJohn Marino 
27950fc853eSJohn Marino 	if (c == -1)
28050fc853eSJohn Marino 		add_leaf(sl, sli);
28150fc853eSJohn Marino 	else
28250fc853eSJohn Marino 		add_to_sublevel(sl, sli, c);
28350fc853eSJohn Marino }
28450fc853eSJohn Marino 
28550fc853eSJohn Marino static void
free_sort_level(struct sort_level * sl)28650fc853eSJohn Marino free_sort_level(struct sort_level *sl)
28750fc853eSJohn Marino {
28850fc853eSJohn Marino 
28950fc853eSJohn Marino 	if (sl) {
29050fc853eSJohn Marino 		if (sl->leaves)
29150fc853eSJohn Marino 			sort_free(sl->leaves);
29250fc853eSJohn Marino 
29350fc853eSJohn Marino 		if (sl->level > 0)
29450fc853eSJohn Marino 			sort_free(sl->tosort);
29550fc853eSJohn Marino 
29650fc853eSJohn Marino 		if (sl->sublevels) {
29750fc853eSJohn Marino 			struct sort_level *slc;
29850fc853eSJohn Marino 			size_t sln;
29950fc853eSJohn Marino 
30050fc853eSJohn Marino 			sln = sl->sln;
30150fc853eSJohn Marino 
30250fc853eSJohn Marino 			for (size_t i = 0; i < sln; ++i) {
30350fc853eSJohn Marino 				slc = sl->sublevels[i];
30450fc853eSJohn Marino 				if (slc)
30550fc853eSJohn Marino 					free_sort_level(slc);
30650fc853eSJohn Marino 			}
30750fc853eSJohn Marino 
30850fc853eSJohn Marino 			sort_free(sl->sublevels);
30950fc853eSJohn Marino 		}
31050fc853eSJohn Marino 
31150fc853eSJohn Marino 		sort_free(sl);
31250fc853eSJohn Marino 	}
31350fc853eSJohn Marino }
31450fc853eSJohn Marino 
31550fc853eSJohn Marino static void
run_sort_level_next(struct sort_level * sl)31650fc853eSJohn Marino run_sort_level_next(struct sort_level *sl)
31750fc853eSJohn Marino {
31850fc853eSJohn Marino 	struct sort_level *slc;
31950fc853eSJohn Marino 	size_t i, sln, tosort_num;
32050fc853eSJohn Marino 
32150fc853eSJohn Marino 	if (sl->sublevels) {
32250fc853eSJohn Marino 		sort_free(sl->sublevels);
32350fc853eSJohn Marino 		sl->sublevels = NULL;
32450fc853eSJohn Marino 	}
32550fc853eSJohn Marino 
32650fc853eSJohn Marino 	switch (sl->tosort_num) {
32750fc853eSJohn Marino 	case 0:
32850fc853eSJohn Marino 		goto end;
32950fc853eSJohn Marino 	case (1):
33050fc853eSJohn Marino 		sl->sorted[sl->start_position] = sl->tosort[0];
33150fc853eSJohn Marino 		sort_left_dec(1);
33250fc853eSJohn Marino 		goto end;
33350fc853eSJohn Marino 	case (2):
33450fc853eSJohn Marino 		if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]),
33550fc853eSJohn Marino 		    sl->level) > 0) {
33650fc853eSJohn Marino 			sl->sorted[sl->start_position++] = sl->tosort[1];
33750fc853eSJohn Marino 			sl->sorted[sl->start_position] = sl->tosort[0];
33850fc853eSJohn Marino 		} else {
33950fc853eSJohn Marino 			sl->sorted[sl->start_position++] = sl->tosort[0];
34050fc853eSJohn Marino 			sl->sorted[sl->start_position] = sl->tosort[1];
34150fc853eSJohn Marino 		}
34250fc853eSJohn Marino 		sort_left_dec(2);
34350fc853eSJohn Marino 
34450fc853eSJohn Marino 		goto end;
34550fc853eSJohn Marino 	default:
34650fc853eSJohn Marino 		if (TINY_NODE(sl) || (sl->level > 15)) {
34750fc853eSJohn Marino 			listcoll_t func;
34850fc853eSJohn Marino 
34950fc853eSJohn Marino 			func = get_list_call_func(sl->level);
35050fc853eSJohn Marino 
35150fc853eSJohn Marino 			sl->leaves = sl->tosort;
35250fc853eSJohn Marino 			sl->leaves_num = sl->tosort_num;
35350fc853eSJohn Marino 			sl->leaves_sz = sl->leaves_num;
35450fc853eSJohn Marino 			sl->leaves = sort_realloc(sl->leaves,
35550fc853eSJohn Marino 			    (sizeof(struct sort_list_item *) *
35650fc853eSJohn Marino 			    (sl->leaves_sz)));
35750fc853eSJohn Marino 			sl->tosort = NULL;
35850fc853eSJohn Marino 			sl->tosort_num = 0;
35950fc853eSJohn Marino 			sl->tosort_sz = 0;
36050fc853eSJohn Marino 			sl->sln = 0;
36150fc853eSJohn Marino 			sl->real_sln = 0;
36250fc853eSJohn Marino 			if (sort_opts_vals.sflag) {
36350fc853eSJohn Marino 				if (mergesort(sl->leaves, sl->leaves_num,
36450fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
36550fc853eSJohn Marino 				    (int(*)(const void *, const void *)) func) == -1)
36650fc853eSJohn Marino 					/* NOTREACHED */
36750fc853eSJohn Marino 					err(2, "Radix sort error 3");
36850fc853eSJohn Marino 			} else
36950fc853eSJohn Marino 				DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
37050fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
37150fc853eSJohn Marino 				    (int(*)(const void *, const void *)) func);
37250fc853eSJohn Marino 
37350fc853eSJohn Marino 			memcpy(sl->sorted + sl->start_position,
37450fc853eSJohn Marino 			    sl->leaves, sl->leaves_num *
37550fc853eSJohn Marino 			    sizeof(struct sort_list_item*));
37650fc853eSJohn Marino 
37750fc853eSJohn Marino 			sort_left_dec(sl->leaves_num);
37850fc853eSJohn Marino 
37950fc853eSJohn Marino 			goto end;
38050fc853eSJohn Marino 		} else {
38150fc853eSJohn Marino 			sl->tosort_sz = sl->tosort_num;
38250fc853eSJohn Marino 			sl->tosort = sort_realloc(sl->tosort,
38350fc853eSJohn Marino 			    sizeof(struct sort_list_item*) * (sl->tosort_sz));
38450fc853eSJohn Marino 		}
38550fc853eSJohn Marino 	}
38650fc853eSJohn Marino 
38750fc853eSJohn Marino 	sl->sln = 256;
38850fc853eSJohn Marino 	sl->sublevels = sort_malloc(slsz);
38950fc853eSJohn Marino 	memset(sl->sublevels, 0, slsz);
39050fc853eSJohn Marino 
39150fc853eSJohn Marino 	sl->real_sln = 0;
39250fc853eSJohn Marino 
39350fc853eSJohn Marino 	tosort_num = sl->tosort_num;
39450fc853eSJohn Marino 	for (i = 0; i < tosort_num; ++i)
39550fc853eSJohn Marino 		place_item(sl, i);
39650fc853eSJohn Marino 
39750fc853eSJohn Marino 	sort_free(sl->tosort);
39850fc853eSJohn Marino 	sl->tosort = NULL;
39950fc853eSJohn Marino 	sl->tosort_num = 0;
40050fc853eSJohn Marino 	sl->tosort_sz = 0;
40150fc853eSJohn Marino 
40250fc853eSJohn Marino 	if (sl->leaves_num > 1) {
40350fc853eSJohn Marino 		if (keys_num > 1) {
40450fc853eSJohn Marino 			if (sort_opts_vals.sflag) {
40550fc853eSJohn Marino 				mergesort(sl->leaves, sl->leaves_num,
40650fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
40750fc853eSJohn Marino 				    (int(*)(const void *, const void *)) list_coll);
40850fc853eSJohn Marino 			} else {
40950fc853eSJohn Marino 				DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
41050fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
41150fc853eSJohn Marino 				    (int(*)(const void *, const void *)) list_coll);
41250fc853eSJohn Marino 			}
41350fc853eSJohn Marino 		} else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
41450fc853eSJohn Marino 			DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
41550fc853eSJohn Marino 			    sizeof(struct sort_list_item *),
41650fc853eSJohn Marino 			    (int(*)(const void *, const void *)) list_coll_by_str_only);
41750fc853eSJohn Marino 		}
41850fc853eSJohn Marino 	}
41950fc853eSJohn Marino 
42050fc853eSJohn Marino 	sl->leaves_sz = sl->leaves_num;
42150fc853eSJohn Marino 	sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) *
42250fc853eSJohn Marino 	    (sl->leaves_sz)));
42350fc853eSJohn Marino 
42450fc853eSJohn Marino 	if (!reverse_sort) {
42550fc853eSJohn Marino 		memcpy(sl->sorted + sl->start_position, sl->leaves,
42650fc853eSJohn Marino 		    sl->leaves_num * sizeof(struct sort_list_item*));
42750fc853eSJohn Marino 		sl->start_position += sl->leaves_num;
42850fc853eSJohn Marino 		sort_left_dec(sl->leaves_num);
42950fc853eSJohn Marino 
43050fc853eSJohn Marino 		sort_free(sl->leaves);
43150fc853eSJohn Marino 		sl->leaves = NULL;
43250fc853eSJohn Marino 		sl->leaves_num = 0;
43350fc853eSJohn Marino 		sl->leaves_sz = 0;
43450fc853eSJohn Marino 
43550fc853eSJohn Marino 		sln = sl->sln;
43650fc853eSJohn Marino 
43750fc853eSJohn Marino 		for (i = 0; i < sln; ++i) {
43850fc853eSJohn Marino 			slc = sl->sublevels[i];
43950fc853eSJohn Marino 
44050fc853eSJohn Marino 			if (slc) {
44150fc853eSJohn Marino 				slc->sorted = sl->sorted;
44250fc853eSJohn Marino 				slc->start_position = sl->start_position;
44350fc853eSJohn Marino 				sl->start_position += slc->tosort_num;
44450fc853eSJohn Marino 				if (SMALL_NODE(slc))
44550fc853eSJohn Marino 					run_sort_level_next(slc);
44650fc853eSJohn Marino 				else
44750fc853eSJohn Marino 					push_ls(slc);
44850fc853eSJohn Marino 				sl->sublevels[i] = NULL;
44950fc853eSJohn Marino 			}
45050fc853eSJohn Marino 		}
45150fc853eSJohn Marino 
45250fc853eSJohn Marino 	} else {
45350fc853eSJohn Marino 		size_t n;
45450fc853eSJohn Marino 
45550fc853eSJohn Marino 		sln = sl->sln;
45650fc853eSJohn Marino 
45750fc853eSJohn Marino 		for (i = 0; i < sln; ++i) {
45850fc853eSJohn Marino 			n = sln - i - 1;
45950fc853eSJohn Marino 			slc = sl->sublevels[n];
46050fc853eSJohn Marino 
46150fc853eSJohn Marino 			if (slc) {
46250fc853eSJohn Marino 				slc->sorted = sl->sorted;
46350fc853eSJohn Marino 				slc->start_position = sl->start_position;
46450fc853eSJohn Marino 				sl->start_position += slc->tosort_num;
46550fc853eSJohn Marino 				if (SMALL_NODE(slc))
46650fc853eSJohn Marino 					run_sort_level_next(slc);
46750fc853eSJohn Marino 				else
46850fc853eSJohn Marino 					push_ls(slc);
46950fc853eSJohn Marino 				sl->sublevels[n] = NULL;
47050fc853eSJohn Marino 			}
47150fc853eSJohn Marino 		}
47250fc853eSJohn Marino 
47350fc853eSJohn Marino 		memcpy(sl->sorted + sl->start_position, sl->leaves,
47450fc853eSJohn Marino 		    sl->leaves_num * sizeof(struct sort_list_item*));
47550fc853eSJohn Marino 		sort_left_dec(sl->leaves_num);
47650fc853eSJohn Marino 	}
47750fc853eSJohn Marino 
47850fc853eSJohn Marino end:
47950fc853eSJohn Marino 	free_sort_level(sl);
48050fc853eSJohn Marino }
48150fc853eSJohn Marino 
48250fc853eSJohn Marino /*
48350fc853eSJohn Marino  * Single-threaded sort cycle
48450fc853eSJohn Marino  */
48550fc853eSJohn Marino static void
run_sort_cycle_st(void)48650fc853eSJohn Marino run_sort_cycle_st(void)
48750fc853eSJohn Marino {
48850fc853eSJohn Marino 	struct sort_level *slc;
48950fc853eSJohn Marino 
49050fc853eSJohn Marino 	for (;;) {
49150fc853eSJohn Marino 		slc = pop_ls_st();
49250fc853eSJohn Marino 		if (slc == NULL) {
49350fc853eSJohn Marino 			break;
49450fc853eSJohn Marino 		}
49550fc853eSJohn Marino 		run_sort_level_next(slc);
49650fc853eSJohn Marino 	}
49750fc853eSJohn Marino }
49850fc853eSJohn Marino 
49950fc853eSJohn Marino #if defined(SORT_THREADS)
50050fc853eSJohn Marino 
50150fc853eSJohn Marino /*
50250fc853eSJohn Marino  * Multi-threaded sort cycle
50350fc853eSJohn Marino  */
50450fc853eSJohn Marino static void
run_sort_cycle_mt(void)50550fc853eSJohn Marino run_sort_cycle_mt(void)
50650fc853eSJohn Marino {
50750fc853eSJohn Marino 	struct sort_level *slc;
50850fc853eSJohn Marino 
50950fc853eSJohn Marino 	for (;;) {
51050fc853eSJohn Marino 		slc = pop_ls_mt();
511*07774aeaSMatthew Dillon 		if (slc == NULL)
51250fc853eSJohn Marino 			break;
51350fc853eSJohn Marino 		run_sort_level_next(slc);
51450fc853eSJohn Marino 	}
51550fc853eSJohn Marino }
51650fc853eSJohn Marino 
51750fc853eSJohn Marino /*
51850fc853eSJohn Marino  * Sort cycle thread (in multi-threaded mode)
51950fc853eSJohn Marino  */
52050fc853eSJohn Marino static void*
sort_thread(void * arg)52150fc853eSJohn Marino sort_thread(void* arg)
52250fc853eSJohn Marino {
52350fc853eSJohn Marino 	run_sort_cycle_mt();
52450fc853eSJohn Marino 	sem_post(&mtsem);
52550fc853eSJohn Marino 
52650fc853eSJohn Marino 	return (arg);
52750fc853eSJohn Marino }
52850fc853eSJohn Marino 
52950fc853eSJohn Marino #endif /* defined(SORT_THREADS) */
53050fc853eSJohn Marino 
53150fc853eSJohn Marino static void
run_top_sort_level(struct sort_level * sl)53250fc853eSJohn Marino run_top_sort_level(struct sort_level *sl)
53350fc853eSJohn Marino {
53450fc853eSJohn Marino 	struct sort_level *slc;
53550fc853eSJohn Marino 
53650fc853eSJohn Marino 	reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag :
53750fc853eSJohn Marino 	    default_sort_mods->rflag;
53850fc853eSJohn Marino 
53950fc853eSJohn Marino 	sl->start_position = 0;
54050fc853eSJohn Marino 	sl->sln = 256;
54150fc853eSJohn Marino 	sl->sublevels = sort_malloc(slsz);
54250fc853eSJohn Marino 	memset(sl->sublevels, 0, slsz);
54350fc853eSJohn Marino 
54450fc853eSJohn Marino 	for (size_t i = 0; i < sl->tosort_num; ++i)
54550fc853eSJohn Marino 		place_item(sl, i);
54650fc853eSJohn Marino 
54750fc853eSJohn Marino 	if (sl->leaves_num > 1) {
54850fc853eSJohn Marino 		if (keys_num > 1) {
54950fc853eSJohn Marino 			if (sort_opts_vals.sflag) {
55050fc853eSJohn Marino 				mergesort(sl->leaves, sl->leaves_num,
55150fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
55250fc853eSJohn Marino 				    (int(*)(const void *, const void *)) list_coll);
55350fc853eSJohn Marino 			} else {
55450fc853eSJohn Marino 				DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
55550fc853eSJohn Marino 				    sizeof(struct sort_list_item *),
55650fc853eSJohn Marino 				    (int(*)(const void *, const void *)) list_coll);
55750fc853eSJohn Marino 			}
55850fc853eSJohn Marino 		} else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
55950fc853eSJohn Marino 			DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
56050fc853eSJohn Marino 			    sizeof(struct sort_list_item *),
56150fc853eSJohn Marino 			    (int(*)(const void *, const void *)) list_coll_by_str_only);
56250fc853eSJohn Marino 		}
56350fc853eSJohn Marino 	}
56450fc853eSJohn Marino 
56550fc853eSJohn Marino 	if (!reverse_sort) {
56650fc853eSJohn Marino 		memcpy(sl->tosort + sl->start_position, sl->leaves,
56750fc853eSJohn Marino 		    sl->leaves_num * sizeof(struct sort_list_item*));
56850fc853eSJohn Marino 		sl->start_position += sl->leaves_num;
56950fc853eSJohn Marino 		sort_left_dec(sl->leaves_num);
57050fc853eSJohn Marino 
57150fc853eSJohn Marino 		for (size_t i = 0; i < sl->sln; ++i) {
57250fc853eSJohn Marino 			slc = sl->sublevels[i];
57350fc853eSJohn Marino 
57450fc853eSJohn Marino 			if (slc) {
57550fc853eSJohn Marino 				slc->sorted = sl->tosort;
57650fc853eSJohn Marino 				slc->start_position = sl->start_position;
57750fc853eSJohn Marino 				sl->start_position += slc->tosort_num;
57850fc853eSJohn Marino 				push_ls(slc);
57950fc853eSJohn Marino 				sl->sublevels[i] = NULL;
58050fc853eSJohn Marino 			}
58150fc853eSJohn Marino 		}
58250fc853eSJohn Marino 
58350fc853eSJohn Marino 	} else {
58450fc853eSJohn Marino 		size_t n;
58550fc853eSJohn Marino 
58650fc853eSJohn Marino 		for (size_t i = 0; i < sl->sln; ++i) {
58750fc853eSJohn Marino 
58850fc853eSJohn Marino 			n = sl->sln - i - 1;
58950fc853eSJohn Marino 			slc = sl->sublevels[n];
59050fc853eSJohn Marino 
59150fc853eSJohn Marino 			if (slc) {
59250fc853eSJohn Marino 				slc->sorted = sl->tosort;
59350fc853eSJohn Marino 				slc->start_position = sl->start_position;
59450fc853eSJohn Marino 				sl->start_position += slc->tosort_num;
59550fc853eSJohn Marino 				push_ls(slc);
59650fc853eSJohn Marino 				sl->sublevels[n] = NULL;
59750fc853eSJohn Marino 			}
59850fc853eSJohn Marino 		}
59950fc853eSJohn Marino 
60050fc853eSJohn Marino 		memcpy(sl->tosort + sl->start_position, sl->leaves,
60150fc853eSJohn Marino 		    sl->leaves_num * sizeof(struct sort_list_item*));
60250fc853eSJohn Marino 
60350fc853eSJohn Marino 		sort_left_dec(sl->leaves_num);
60450fc853eSJohn Marino 	}
60550fc853eSJohn Marino 
60650fc853eSJohn Marino #if defined(SORT_THREADS)
60750fc853eSJohn Marino 	if (nthreads < 2) {
60850fc853eSJohn Marino #endif
60950fc853eSJohn Marino 		run_sort_cycle_st();
61050fc853eSJohn Marino #if defined(SORT_THREADS)
61150fc853eSJohn Marino 	} else {
61250fc853eSJohn Marino 		size_t i;
61350fc853eSJohn Marino 
61450fc853eSJohn Marino 		for(i = 0; i < nthreads; ++i) {
61550fc853eSJohn Marino 			pthread_attr_t attr;
61650fc853eSJohn Marino 			pthread_t pth;
61750fc853eSJohn Marino 
61850fc853eSJohn Marino 			pthread_attr_init(&attr);
619*07774aeaSMatthew Dillon 			pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED);
62050fc853eSJohn Marino 
62150fc853eSJohn Marino 			for (;;) {
62250fc853eSJohn Marino 				int res = pthread_create(&pth, &attr,
62350fc853eSJohn Marino 							 sort_thread, NULL);
62450fc853eSJohn Marino 				if (res >= 0)
62550fc853eSJohn Marino 					break;
62650fc853eSJohn Marino 				if (errno == EAGAIN) {
62750fc853eSJohn Marino 					pthread_yield();
62850fc853eSJohn Marino 					continue;
62950fc853eSJohn Marino 				}
63050fc853eSJohn Marino 				err(2, NULL);
63150fc853eSJohn Marino 			}
63250fc853eSJohn Marino 
63350fc853eSJohn Marino 			pthread_attr_destroy(&attr);
63450fc853eSJohn Marino 		}
63550fc853eSJohn Marino 
63650fc853eSJohn Marino 		for (i = 0; i < nthreads; ++i)
63750fc853eSJohn Marino 			sem_wait(&mtsem);
63850fc853eSJohn Marino 	}
63950fc853eSJohn Marino #endif /* defined(SORT_THREADS) */
64050fc853eSJohn Marino }
64150fc853eSJohn Marino 
64250fc853eSJohn Marino static void
run_sort(struct sort_list_item ** base,size_t nmemb)64350fc853eSJohn Marino run_sort(struct sort_list_item **base, size_t nmemb)
64450fc853eSJohn Marino {
64550fc853eSJohn Marino 	struct sort_level *sl;
64650fc853eSJohn Marino 
64750fc853eSJohn Marino #if defined(SORT_THREADS)
64850fc853eSJohn Marino 	size_t nthreads_save = nthreads;
64950fc853eSJohn Marino 	if (nmemb < MT_SORT_THRESHOLD)
65050fc853eSJohn Marino 		nthreads = 1;
65150fc853eSJohn Marino 
65250fc853eSJohn Marino 	if (nthreads > 1) {
65350fc853eSJohn Marino 		pthread_mutexattr_t mattr;
65450fc853eSJohn Marino 
65550fc853eSJohn Marino 		pthread_mutexattr_init(&mattr);
65650fc853eSJohn Marino 		pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK);
65750fc853eSJohn Marino 
65850fc853eSJohn Marino 		pthread_mutex_init(&g_ls_mutex, &mattr);
659*07774aeaSMatthew Dillon 		pthread_cond_init(&g_ls_cond, NULL);
66050fc853eSJohn Marino 
66150fc853eSJohn Marino 		pthread_mutexattr_destroy(&mattr);
66250fc853eSJohn Marino 
66350fc853eSJohn Marino 		sem_init(&mtsem, 0, 0);
66450fc853eSJohn Marino 
66550fc853eSJohn Marino 	}
66650fc853eSJohn Marino #endif
66750fc853eSJohn Marino 
66850fc853eSJohn Marino 	sl = sort_malloc(sizeof(struct sort_level));
66950fc853eSJohn Marino 	memset(sl, 0, sizeof(struct sort_level));
67050fc853eSJohn Marino 
67150fc853eSJohn Marino 	sl->tosort = base;
67250fc853eSJohn Marino 	sl->tosort_num = nmemb;
67350fc853eSJohn Marino 	sl->tosort_sz = nmemb;
67450fc853eSJohn Marino 
67550fc853eSJohn Marino #if defined(SORT_THREADS)
67650fc853eSJohn Marino 	sort_left = nmemb;
67750fc853eSJohn Marino #endif
67850fc853eSJohn Marino 
67950fc853eSJohn Marino 	run_top_sort_level(sl);
68050fc853eSJohn Marino 
68150fc853eSJohn Marino 	free_sort_level(sl);
68250fc853eSJohn Marino 
68350fc853eSJohn Marino #if defined(SORT_THREADS)
68450fc853eSJohn Marino 	if (nthreads > 1) {
68550fc853eSJohn Marino 		sem_destroy(&mtsem);
68650fc853eSJohn Marino 		pthread_mutex_destroy(&g_ls_mutex);
68750fc853eSJohn Marino 	}
68850fc853eSJohn Marino 	nthreads = nthreads_save;
68950fc853eSJohn Marino #endif
69050fc853eSJohn Marino }
69150fc853eSJohn Marino 
69250fc853eSJohn Marino void
rxsort(struct sort_list_item ** base,size_t nmemb)69350fc853eSJohn Marino rxsort(struct sort_list_item **base, size_t nmemb)
69450fc853eSJohn Marino {
69550fc853eSJohn Marino 
69650fc853eSJohn Marino 	run_sort(base, nmemb);
69750fc853eSJohn Marino }
698