1c66bbc91SGabor Kovesdan /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
4c859c6ddSGabor Kovesdan * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
5c66bbc91SGabor Kovesdan * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
6c66bbc91SGabor Kovesdan * All rights reserved.
7c66bbc91SGabor Kovesdan *
8c66bbc91SGabor Kovesdan * Redistribution and use in source and binary forms, with or without
9c66bbc91SGabor Kovesdan * modification, are permitted provided that the following conditions
10c66bbc91SGabor Kovesdan * are met:
11c66bbc91SGabor Kovesdan * 1. Redistributions of source code must retain the above copyright
12c66bbc91SGabor Kovesdan * notice, this list of conditions and the following disclaimer.
13c66bbc91SGabor Kovesdan * 2. Redistributions in binary form must reproduce the above copyright
14c66bbc91SGabor Kovesdan * notice, this list of conditions and the following disclaimer in the
15c66bbc91SGabor Kovesdan * documentation and/or other materials provided with the distribution.
16c66bbc91SGabor Kovesdan *
17c66bbc91SGabor Kovesdan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18c66bbc91SGabor Kovesdan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19c66bbc91SGabor Kovesdan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20c66bbc91SGabor Kovesdan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21c66bbc91SGabor Kovesdan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22c66bbc91SGabor Kovesdan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23c66bbc91SGabor Kovesdan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24c66bbc91SGabor Kovesdan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25c66bbc91SGabor Kovesdan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26c66bbc91SGabor Kovesdan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27c66bbc91SGabor Kovesdan * SUCH DAMAGE.
28c66bbc91SGabor Kovesdan */
29c66bbc91SGabor Kovesdan
30c66bbc91SGabor Kovesdan #include <sys/cdefs.h>
31c66bbc91SGabor Kovesdan #include <errno.h>
32c66bbc91SGabor Kovesdan #include <err.h>
33c66bbc91SGabor Kovesdan #include <langinfo.h>
34c66bbc91SGabor Kovesdan #include <math.h>
35c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
36c66bbc91SGabor Kovesdan #include <pthread.h>
37c66bbc91SGabor Kovesdan #include <semaphore.h>
38c66bbc91SGabor Kovesdan #endif
39c66bbc91SGabor Kovesdan #include <stdlib.h>
40c66bbc91SGabor Kovesdan #include <string.h>
41c66bbc91SGabor Kovesdan #include <wchar.h>
42c66bbc91SGabor Kovesdan #include <wctype.h>
43c66bbc91SGabor Kovesdan #include <unistd.h>
44c66bbc91SGabor Kovesdan
45c66bbc91SGabor Kovesdan #include "coll.h"
46c66bbc91SGabor Kovesdan #include "radixsort.h"
47c66bbc91SGabor Kovesdan
485d5151aeSGabor Kovesdan #define DEFAULT_SORT_FUNC_RADIXSORT mergesort
495d5151aeSGabor Kovesdan
50c66bbc91SGabor Kovesdan #define TINY_NODE(sl) ((sl)->tosort_num < 65)
51c66bbc91SGabor Kovesdan #define SMALL_NODE(sl) ((sl)->tosort_num < 5)
52c66bbc91SGabor Kovesdan
53c66bbc91SGabor Kovesdan /* are we sorting in reverse order ? */
54ce1e997fSGabor Kovesdan static bool reverse_sort;
55c66bbc91SGabor Kovesdan
56c66bbc91SGabor Kovesdan /* sort sub-levels array size */
57c66bbc91SGabor Kovesdan static const size_t slsz = 256 * sizeof(struct sort_level*);
58c66bbc91SGabor Kovesdan
59c66bbc91SGabor Kovesdan /* one sort level structure */
60c66bbc91SGabor Kovesdan struct sort_level
61c66bbc91SGabor Kovesdan {
62c66bbc91SGabor Kovesdan struct sort_level **sublevels;
63c66bbc91SGabor Kovesdan struct sort_list_item **leaves;
64c66bbc91SGabor Kovesdan struct sort_list_item **sorted;
65c66bbc91SGabor Kovesdan struct sort_list_item **tosort;
66c66bbc91SGabor Kovesdan size_t leaves_num;
67c66bbc91SGabor Kovesdan size_t leaves_sz;
68c66bbc91SGabor Kovesdan size_t level;
69c66bbc91SGabor Kovesdan size_t real_sln;
70c66bbc91SGabor Kovesdan size_t start_position;
71c66bbc91SGabor Kovesdan size_t sln;
72c66bbc91SGabor Kovesdan size_t tosort_num;
73c66bbc91SGabor Kovesdan size_t tosort_sz;
74c66bbc91SGabor Kovesdan };
75c66bbc91SGabor Kovesdan
76c66bbc91SGabor Kovesdan /* stack of sort levels ready to be sorted */
77c66bbc91SGabor Kovesdan struct level_stack {
78c66bbc91SGabor Kovesdan struct level_stack *next;
79c66bbc91SGabor Kovesdan struct sort_level *sl;
80c66bbc91SGabor Kovesdan };
81c66bbc91SGabor Kovesdan
82ce1e997fSGabor Kovesdan static struct level_stack *g_ls;
83c66bbc91SGabor Kovesdan
84c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
85c66bbc91SGabor Kovesdan /* stack guarding mutex */
86692cd1a3SPedro F. Giffuni static pthread_cond_t g_ls_cond;
87c66bbc91SGabor Kovesdan static pthread_mutex_t g_ls_mutex;
88c66bbc91SGabor Kovesdan
89c66bbc91SGabor Kovesdan /* counter: how many items are left */
90ce1e997fSGabor Kovesdan static size_t sort_left;
91c66bbc91SGabor Kovesdan /* guarding mutex */
92c66bbc91SGabor Kovesdan
93c66bbc91SGabor Kovesdan /* semaphore to count threads */
94c66bbc91SGabor Kovesdan static sem_t mtsem;
95c66bbc91SGabor Kovesdan
96c66bbc91SGabor Kovesdan /*
97c66bbc91SGabor Kovesdan * Decrement items counter
98c66bbc91SGabor Kovesdan */
99c66bbc91SGabor Kovesdan static inline void
sort_left_dec(size_t n)100c66bbc91SGabor Kovesdan sort_left_dec(size_t n)
101c66bbc91SGabor Kovesdan {
102692cd1a3SPedro F. Giffuni pthread_mutex_lock(&g_ls_mutex);
103c66bbc91SGabor Kovesdan sort_left -= n;
104692cd1a3SPedro F. Giffuni if (sort_left == 0 && nthreads > 1)
105692cd1a3SPedro F. Giffuni pthread_cond_broadcast(&g_ls_cond);
106692cd1a3SPedro F. Giffuni pthread_mutex_unlock(&g_ls_mutex);
107c66bbc91SGabor Kovesdan }
108c66bbc91SGabor Kovesdan
109c66bbc91SGabor Kovesdan /*
110c66bbc91SGabor Kovesdan * Do we have something to sort ?
111692cd1a3SPedro F. Giffuni *
112692cd1a3SPedro F. Giffuni * This routine does not need to be locked.
113c66bbc91SGabor Kovesdan */
114c66bbc91SGabor Kovesdan static inline bool
have_sort_left(void)115c66bbc91SGabor Kovesdan have_sort_left(void)
116c66bbc91SGabor Kovesdan {
117c66bbc91SGabor Kovesdan bool ret;
118c66bbc91SGabor Kovesdan
119c66bbc91SGabor Kovesdan ret = (sort_left > 0);
120692cd1a3SPedro F. Giffuni
121c66bbc91SGabor Kovesdan return (ret);
122c66bbc91SGabor Kovesdan }
123c66bbc91SGabor Kovesdan
124c66bbc91SGabor Kovesdan #else
125c66bbc91SGabor Kovesdan
126c66bbc91SGabor Kovesdan #define sort_left_dec(n)
127c66bbc91SGabor Kovesdan
128c66bbc91SGabor Kovesdan #endif /* SORT_THREADS */
129c66bbc91SGabor Kovesdan
1307f180c0fSMark Johnston static void
_push_ls(struct level_stack * ls)1317f180c0fSMark Johnston _push_ls(struct level_stack *ls)
1327f180c0fSMark Johnston {
1337f180c0fSMark Johnston
1347f180c0fSMark Johnston ls->next = g_ls;
1357f180c0fSMark Johnston g_ls = ls;
1367f180c0fSMark Johnston }
1377f180c0fSMark Johnston
138c66bbc91SGabor Kovesdan /*
139c66bbc91SGabor Kovesdan * Push sort level to the stack
140c66bbc91SGabor Kovesdan */
141c66bbc91SGabor Kovesdan static inline void
push_ls(struct sort_level * sl)142c66bbc91SGabor Kovesdan push_ls(struct sort_level *sl)
143c66bbc91SGabor Kovesdan {
144c66bbc91SGabor Kovesdan struct level_stack *new_ls;
145c66bbc91SGabor Kovesdan
146c66bbc91SGabor Kovesdan new_ls = sort_malloc(sizeof(struct level_stack));
147c66bbc91SGabor Kovesdan new_ls->sl = sl;
148c66bbc91SGabor Kovesdan
149c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
1507f180c0fSMark Johnston if (nthreads > 1) {
151c66bbc91SGabor Kovesdan pthread_mutex_lock(&g_ls_mutex);
1527f180c0fSMark Johnston _push_ls(new_ls);
153692cd1a3SPedro F. Giffuni pthread_cond_signal(&g_ls_cond);
154c66bbc91SGabor Kovesdan pthread_mutex_unlock(&g_ls_mutex);
1557f180c0fSMark Johnston } else
156c66bbc91SGabor Kovesdan #endif
1577f180c0fSMark Johnston _push_ls(new_ls);
158c66bbc91SGabor Kovesdan }
159c66bbc91SGabor Kovesdan
160c66bbc91SGabor Kovesdan /*
161c66bbc91SGabor Kovesdan * Pop sort level from the stack (single-threaded style)
162c66bbc91SGabor Kovesdan */
163c66bbc91SGabor Kovesdan static inline struct sort_level*
pop_ls_st(void)164c66bbc91SGabor Kovesdan pop_ls_st(void)
165c66bbc91SGabor Kovesdan {
166c66bbc91SGabor Kovesdan struct sort_level *sl;
167c66bbc91SGabor Kovesdan
168c66bbc91SGabor Kovesdan if (g_ls) {
169c66bbc91SGabor Kovesdan struct level_stack *saved_ls;
170c66bbc91SGabor Kovesdan
171c66bbc91SGabor Kovesdan sl = g_ls->sl;
172c66bbc91SGabor Kovesdan saved_ls = g_ls;
173c66bbc91SGabor Kovesdan g_ls = g_ls->next;
174c66bbc91SGabor Kovesdan sort_free(saved_ls);
175c66bbc91SGabor Kovesdan } else
176c66bbc91SGabor Kovesdan sl = NULL;
177c66bbc91SGabor Kovesdan
178c66bbc91SGabor Kovesdan return (sl);
179c66bbc91SGabor Kovesdan }
180c66bbc91SGabor Kovesdan
18177f98fbbSDimitry Andric #if defined(SORT_THREADS)
18277f98fbbSDimitry Andric
183c66bbc91SGabor Kovesdan /*
184c66bbc91SGabor Kovesdan * Pop sort level from the stack (multi-threaded style)
185c66bbc91SGabor Kovesdan */
186c66bbc91SGabor Kovesdan static inline struct sort_level*
pop_ls_mt(void)187c66bbc91SGabor Kovesdan pop_ls_mt(void)
188c66bbc91SGabor Kovesdan {
189c66bbc91SGabor Kovesdan struct level_stack *saved_ls;
190c66bbc91SGabor Kovesdan struct sort_level *sl;
191c66bbc91SGabor Kovesdan
192c66bbc91SGabor Kovesdan pthread_mutex_lock(&g_ls_mutex);
193c66bbc91SGabor Kovesdan
194692cd1a3SPedro F. Giffuni for (;;) {
195c66bbc91SGabor Kovesdan if (g_ls) {
196c66bbc91SGabor Kovesdan sl = g_ls->sl;
197c66bbc91SGabor Kovesdan saved_ls = g_ls;
198c66bbc91SGabor Kovesdan g_ls = g_ls->next;
199692cd1a3SPedro F. Giffuni break;
200692cd1a3SPedro F. Giffuni }
201c66bbc91SGabor Kovesdan sl = NULL;
202c66bbc91SGabor Kovesdan saved_ls = NULL;
203692cd1a3SPedro F. Giffuni
204692cd1a3SPedro F. Giffuni if (have_sort_left() == 0)
205692cd1a3SPedro F. Giffuni break;
206692cd1a3SPedro F. Giffuni pthread_cond_wait(&g_ls_cond, &g_ls_mutex);
207c66bbc91SGabor Kovesdan }
208c66bbc91SGabor Kovesdan
209c66bbc91SGabor Kovesdan pthread_mutex_unlock(&g_ls_mutex);
210c66bbc91SGabor Kovesdan
211c66bbc91SGabor Kovesdan sort_free(saved_ls);
212c66bbc91SGabor Kovesdan
213c66bbc91SGabor Kovesdan return (sl);
214c66bbc91SGabor Kovesdan }
215c66bbc91SGabor Kovesdan
21677f98fbbSDimitry Andric #endif /* defined(SORT_THREADS) */
21777f98fbbSDimitry Andric
218c66bbc91SGabor Kovesdan static void
add_to_sublevel(struct sort_level * sl,struct sort_list_item * item,size_t indx)219e8da8c74SGabor Kovesdan add_to_sublevel(struct sort_level *sl, struct sort_list_item *item, size_t indx)
220c66bbc91SGabor Kovesdan {
221c66bbc91SGabor Kovesdan struct sort_level *ssl;
222c66bbc91SGabor Kovesdan
223c66bbc91SGabor Kovesdan ssl = sl->sublevels[indx];
224c66bbc91SGabor Kovesdan
225c66bbc91SGabor Kovesdan if (ssl == NULL) {
226ecc3c291SBaptiste Daroussin ssl = sort_calloc(1, sizeof(struct sort_level));
227c66bbc91SGabor Kovesdan
228c66bbc91SGabor Kovesdan ssl->level = sl->level + 1;
229c66bbc91SGabor Kovesdan sl->sublevels[indx] = ssl;
230c66bbc91SGabor Kovesdan
231c66bbc91SGabor Kovesdan ++(sl->real_sln);
232c66bbc91SGabor Kovesdan }
233c66bbc91SGabor Kovesdan
234c66bbc91SGabor Kovesdan if (++(ssl->tosort_num) > ssl->tosort_sz) {
235c66bbc91SGabor Kovesdan ssl->tosort_sz = ssl->tosort_num + 128;
236c66bbc91SGabor Kovesdan ssl->tosort = sort_realloc(ssl->tosort,
237c66bbc91SGabor Kovesdan sizeof(struct sort_list_item*) * (ssl->tosort_sz));
238c66bbc91SGabor Kovesdan }
239c66bbc91SGabor Kovesdan
240c66bbc91SGabor Kovesdan ssl->tosort[ssl->tosort_num - 1] = item;
241c66bbc91SGabor Kovesdan }
242c66bbc91SGabor Kovesdan
243c66bbc91SGabor Kovesdan static inline void
add_leaf(struct sort_level * sl,struct sort_list_item * item)244c66bbc91SGabor Kovesdan add_leaf(struct sort_level *sl, struct sort_list_item *item)
245c66bbc91SGabor Kovesdan {
246e5f71a07SPedro F. Giffuni
247c66bbc91SGabor Kovesdan if (++(sl->leaves_num) > sl->leaves_sz) {
248c66bbc91SGabor Kovesdan sl->leaves_sz = sl->leaves_num + 128;
249c66bbc91SGabor Kovesdan sl->leaves = sort_realloc(sl->leaves,
250c66bbc91SGabor Kovesdan (sizeof(struct sort_list_item*) * (sl->leaves_sz)));
251c66bbc91SGabor Kovesdan }
252c66bbc91SGabor Kovesdan sl->leaves[sl->leaves_num - 1] = item;
253c66bbc91SGabor Kovesdan }
254c66bbc91SGabor Kovesdan
255c66bbc91SGabor Kovesdan static inline int
get_wc_index(struct sort_list_item * sli,size_t level)256c66bbc91SGabor Kovesdan get_wc_index(struct sort_list_item *sli, size_t level)
257c66bbc91SGabor Kovesdan {
25871ec05a2SCyril Zhang const size_t wcfact = (mb_cur_max == 1) ? 1 : sizeof(wchar_t);
259ed7aec1eSMarius Strobl const struct key_value *kv;
260c66bbc91SGabor Kovesdan const struct bwstring *bws;
261c66bbc91SGabor Kovesdan
262ed7aec1eSMarius Strobl kv = get_key_from_keys_array(&sli->ka, 0);
263ed7aec1eSMarius Strobl bws = kv->k;
264c66bbc91SGabor Kovesdan
2659f7e5bdaSConrad Meyer if ((BWSLEN(bws) * wcfact > level)) {
2669f7e5bdaSConrad Meyer wchar_t res;
2679f7e5bdaSConrad Meyer
2689f7e5bdaSConrad Meyer /*
2699f7e5bdaSConrad Meyer * Sort wchar strings a byte at a time, rather than a single
2709f7e5bdaSConrad Meyer * byte from each wchar.
2719f7e5bdaSConrad Meyer */
2729f7e5bdaSConrad Meyer res = (wchar_t)BWS_GET(bws, level / wcfact);
2739f7e5bdaSConrad Meyer /* Sort most-significant byte first. */
2749f7e5bdaSConrad Meyer if (level % wcfact < wcfact - 1)
2759f7e5bdaSConrad Meyer res = (res >> (8 * (wcfact - 1 - (level % wcfact))));
2769f7e5bdaSConrad Meyer
2779f7e5bdaSConrad Meyer return (res & 0xff);
2789f7e5bdaSConrad Meyer }
2799f7e5bdaSConrad Meyer
280c66bbc91SGabor Kovesdan return (-1);
281c66bbc91SGabor Kovesdan }
282c66bbc91SGabor Kovesdan
283c66bbc91SGabor Kovesdan static void
place_item(struct sort_level * sl,size_t item)284c66bbc91SGabor Kovesdan place_item(struct sort_level *sl, size_t item)
285c66bbc91SGabor Kovesdan {
286c66bbc91SGabor Kovesdan struct sort_list_item *sli;
287c66bbc91SGabor Kovesdan int c;
288c66bbc91SGabor Kovesdan
289c66bbc91SGabor Kovesdan sli = sl->tosort[item];
290c66bbc91SGabor Kovesdan c = get_wc_index(sli, sl->level);
291c66bbc91SGabor Kovesdan
292c66bbc91SGabor Kovesdan if (c == -1)
293c66bbc91SGabor Kovesdan add_leaf(sl, sli);
294c66bbc91SGabor Kovesdan else
295c66bbc91SGabor Kovesdan add_to_sublevel(sl, sli, c);
296c66bbc91SGabor Kovesdan }
297c66bbc91SGabor Kovesdan
298c66bbc91SGabor Kovesdan static void
free_sort_level(struct sort_level * sl)299c66bbc91SGabor Kovesdan free_sort_level(struct sort_level *sl)
300c66bbc91SGabor Kovesdan {
301e5f71a07SPedro F. Giffuni
302c66bbc91SGabor Kovesdan if (sl) {
303c66bbc91SGabor Kovesdan if (sl->leaves)
304c66bbc91SGabor Kovesdan sort_free(sl->leaves);
305c66bbc91SGabor Kovesdan
306c66bbc91SGabor Kovesdan if (sl->level > 0)
307c66bbc91SGabor Kovesdan sort_free(sl->tosort);
308c66bbc91SGabor Kovesdan
309c66bbc91SGabor Kovesdan if (sl->sublevels) {
310c66bbc91SGabor Kovesdan struct sort_level *slc;
311c66bbc91SGabor Kovesdan size_t sln;
312c66bbc91SGabor Kovesdan
313c66bbc91SGabor Kovesdan sln = sl->sln;
314c66bbc91SGabor Kovesdan
315c66bbc91SGabor Kovesdan for (size_t i = 0; i < sln; ++i) {
316c66bbc91SGabor Kovesdan slc = sl->sublevels[i];
317c66bbc91SGabor Kovesdan if (slc)
318c66bbc91SGabor Kovesdan free_sort_level(slc);
319c66bbc91SGabor Kovesdan }
320c66bbc91SGabor Kovesdan
321c66bbc91SGabor Kovesdan sort_free(sl->sublevels);
322c66bbc91SGabor Kovesdan }
323c66bbc91SGabor Kovesdan
324c66bbc91SGabor Kovesdan sort_free(sl);
325c66bbc91SGabor Kovesdan }
326c66bbc91SGabor Kovesdan }
327c66bbc91SGabor Kovesdan
328c66bbc91SGabor Kovesdan static void
run_sort_level_next(struct sort_level * sl)329c66bbc91SGabor Kovesdan run_sort_level_next(struct sort_level *sl)
330c66bbc91SGabor Kovesdan {
33171ec05a2SCyril Zhang const size_t wcfact = (mb_cur_max == 1) ? 1 : sizeof(wchar_t);
332c66bbc91SGabor Kovesdan struct sort_level *slc;
333c66bbc91SGabor Kovesdan size_t i, sln, tosort_num;
334c66bbc91SGabor Kovesdan
335c66bbc91SGabor Kovesdan if (sl->sublevels) {
336c66bbc91SGabor Kovesdan sort_free(sl->sublevels);
337c66bbc91SGabor Kovesdan sl->sublevels = NULL;
338c66bbc91SGabor Kovesdan }
339c66bbc91SGabor Kovesdan
340c66bbc91SGabor Kovesdan switch (sl->tosort_num) {
341c66bbc91SGabor Kovesdan case 0:
342c66bbc91SGabor Kovesdan goto end;
343c66bbc91SGabor Kovesdan case (1):
344c66bbc91SGabor Kovesdan sl->sorted[sl->start_position] = sl->tosort[0];
345c66bbc91SGabor Kovesdan sort_left_dec(1);
346c66bbc91SGabor Kovesdan goto end;
347c66bbc91SGabor Kovesdan case (2):
3489f7e5bdaSConrad Meyer /*
3499f7e5bdaSConrad Meyer * Radixsort only processes a single byte at a time. In wchar
3509f7e5bdaSConrad Meyer * mode, this can be a subset of the length of a character.
3519f7e5bdaSConrad Meyer * list_coll_offset() offset is in units of wchar, not bytes.
3529f7e5bdaSConrad Meyer * So to calculate the offset, we must divide by
3539f7e5bdaSConrad Meyer * sizeof(wchar_t) and round down to the index of the first
3549f7e5bdaSConrad Meyer * character this level references.
3559f7e5bdaSConrad Meyer */
356c66bbc91SGabor Kovesdan if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]),
3579f7e5bdaSConrad Meyer sl->level / wcfact) > 0) {
358c66bbc91SGabor Kovesdan sl->sorted[sl->start_position++] = sl->tosort[1];
359c66bbc91SGabor Kovesdan sl->sorted[sl->start_position] = sl->tosort[0];
360c66bbc91SGabor Kovesdan } else {
361c66bbc91SGabor Kovesdan sl->sorted[sl->start_position++] = sl->tosort[0];
362c66bbc91SGabor Kovesdan sl->sorted[sl->start_position] = sl->tosort[1];
363c66bbc91SGabor Kovesdan }
364c66bbc91SGabor Kovesdan sort_left_dec(2);
365c66bbc91SGabor Kovesdan
366c66bbc91SGabor Kovesdan goto end;
367c66bbc91SGabor Kovesdan default:
368c66bbc91SGabor Kovesdan if (TINY_NODE(sl) || (sl->level > 15)) {
369c66bbc91SGabor Kovesdan listcoll_t func;
370c66bbc91SGabor Kovesdan
3719f7e5bdaSConrad Meyer /*
3729f7e5bdaSConrad Meyer * Collate comparison offset is in units of
3739f7e5bdaSConrad Meyer * character-width, so we must divide the level (bytes)
3749f7e5bdaSConrad Meyer * by operating character width (wchar_t or char). See
3759f7e5bdaSConrad Meyer * longer comment above.
3769f7e5bdaSConrad Meyer */
3779f7e5bdaSConrad Meyer func = get_list_call_func(sl->level / wcfact);
378c66bbc91SGabor Kovesdan
379c66bbc91SGabor Kovesdan sl->leaves = sl->tosort;
380c66bbc91SGabor Kovesdan sl->leaves_num = sl->tosort_num;
381c66bbc91SGabor Kovesdan sl->leaves_sz = sl->leaves_num;
382c66bbc91SGabor Kovesdan sl->leaves = sort_realloc(sl->leaves,
383c66bbc91SGabor Kovesdan (sizeof(struct sort_list_item *) *
384c66bbc91SGabor Kovesdan (sl->leaves_sz)));
385c66bbc91SGabor Kovesdan sl->tosort = NULL;
386c66bbc91SGabor Kovesdan sl->tosort_num = 0;
387c66bbc91SGabor Kovesdan sl->tosort_sz = 0;
388c66bbc91SGabor Kovesdan sl->sln = 0;
389c66bbc91SGabor Kovesdan sl->real_sln = 0;
390c66bbc91SGabor Kovesdan if (sort_opts_vals.sflag) {
391c66bbc91SGabor Kovesdan if (mergesort(sl->leaves, sl->leaves_num,
392c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
393c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) func) == -1)
394c66bbc91SGabor Kovesdan /* NOTREACHED */
395c66bbc91SGabor Kovesdan err(2, "Radix sort error 3");
396c66bbc91SGabor Kovesdan } else
3975d5151aeSGabor Kovesdan DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
398c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
399c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) func);
400c66bbc91SGabor Kovesdan
401c66bbc91SGabor Kovesdan memcpy(sl->sorted + sl->start_position,
402c66bbc91SGabor Kovesdan sl->leaves, sl->leaves_num *
403c66bbc91SGabor Kovesdan sizeof(struct sort_list_item*));
404c66bbc91SGabor Kovesdan
405c66bbc91SGabor Kovesdan sort_left_dec(sl->leaves_num);
406c66bbc91SGabor Kovesdan
407c66bbc91SGabor Kovesdan goto end;
408c66bbc91SGabor Kovesdan } else {
409c66bbc91SGabor Kovesdan sl->tosort_sz = sl->tosort_num;
410c66bbc91SGabor Kovesdan sl->tosort = sort_realloc(sl->tosort,
411c66bbc91SGabor Kovesdan sizeof(struct sort_list_item*) * (sl->tosort_sz));
412c66bbc91SGabor Kovesdan }
413c66bbc91SGabor Kovesdan }
414c66bbc91SGabor Kovesdan
415c66bbc91SGabor Kovesdan sl->sln = 256;
416ecc3c291SBaptiste Daroussin sl->sublevels = sort_calloc(1, slsz);
417c66bbc91SGabor Kovesdan
418c66bbc91SGabor Kovesdan sl->real_sln = 0;
419c66bbc91SGabor Kovesdan
420c66bbc91SGabor Kovesdan tosort_num = sl->tosort_num;
421c66bbc91SGabor Kovesdan for (i = 0; i < tosort_num; ++i)
422c66bbc91SGabor Kovesdan place_item(sl, i);
423c66bbc91SGabor Kovesdan
424c66bbc91SGabor Kovesdan sort_free(sl->tosort);
425c66bbc91SGabor Kovesdan sl->tosort = NULL;
426c66bbc91SGabor Kovesdan sl->tosort_num = 0;
427c66bbc91SGabor Kovesdan sl->tosort_sz = 0;
428c66bbc91SGabor Kovesdan
429c66bbc91SGabor Kovesdan if (sl->leaves_num > 1) {
430c66bbc91SGabor Kovesdan if (keys_num > 1) {
431c66bbc91SGabor Kovesdan if (sort_opts_vals.sflag) {
432c66bbc91SGabor Kovesdan mergesort(sl->leaves, sl->leaves_num,
433c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
434c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll);
435c66bbc91SGabor Kovesdan } else {
4365d5151aeSGabor Kovesdan DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
437c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
438c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll);
439c66bbc91SGabor Kovesdan }
440c66bbc91SGabor Kovesdan } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
4415d5151aeSGabor Kovesdan DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
442c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
443c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll_by_str_only);
444c66bbc91SGabor Kovesdan }
445c66bbc91SGabor Kovesdan }
446c66bbc91SGabor Kovesdan
447c66bbc91SGabor Kovesdan sl->leaves_sz = sl->leaves_num;
448c66bbc91SGabor Kovesdan sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) *
449c66bbc91SGabor Kovesdan (sl->leaves_sz)));
450c66bbc91SGabor Kovesdan
451c66bbc91SGabor Kovesdan if (!reverse_sort) {
452c66bbc91SGabor Kovesdan memcpy(sl->sorted + sl->start_position, sl->leaves,
453c66bbc91SGabor Kovesdan sl->leaves_num * sizeof(struct sort_list_item*));
454c66bbc91SGabor Kovesdan sl->start_position += sl->leaves_num;
455c66bbc91SGabor Kovesdan sort_left_dec(sl->leaves_num);
456c66bbc91SGabor Kovesdan
457c66bbc91SGabor Kovesdan sort_free(sl->leaves);
458c66bbc91SGabor Kovesdan sl->leaves = NULL;
459c66bbc91SGabor Kovesdan sl->leaves_num = 0;
460c66bbc91SGabor Kovesdan sl->leaves_sz = 0;
461c66bbc91SGabor Kovesdan
462c66bbc91SGabor Kovesdan sln = sl->sln;
463c66bbc91SGabor Kovesdan
464c66bbc91SGabor Kovesdan for (i = 0; i < sln; ++i) {
465c66bbc91SGabor Kovesdan slc = sl->sublevels[i];
466c66bbc91SGabor Kovesdan
467c66bbc91SGabor Kovesdan if (slc) {
468c66bbc91SGabor Kovesdan slc->sorted = sl->sorted;
469c66bbc91SGabor Kovesdan slc->start_position = sl->start_position;
470c66bbc91SGabor Kovesdan sl->start_position += slc->tosort_num;
471c66bbc91SGabor Kovesdan if (SMALL_NODE(slc))
472c66bbc91SGabor Kovesdan run_sort_level_next(slc);
473c66bbc91SGabor Kovesdan else
474c66bbc91SGabor Kovesdan push_ls(slc);
475c66bbc91SGabor Kovesdan sl->sublevels[i] = NULL;
476c66bbc91SGabor Kovesdan }
477c66bbc91SGabor Kovesdan }
478c66bbc91SGabor Kovesdan
479c66bbc91SGabor Kovesdan } else {
480c66bbc91SGabor Kovesdan size_t n;
481c66bbc91SGabor Kovesdan
482c66bbc91SGabor Kovesdan sln = sl->sln;
483c66bbc91SGabor Kovesdan
484c66bbc91SGabor Kovesdan for (i = 0; i < sln; ++i) {
485c66bbc91SGabor Kovesdan n = sln - i - 1;
486c66bbc91SGabor Kovesdan slc = sl->sublevels[n];
487c66bbc91SGabor Kovesdan
488c66bbc91SGabor Kovesdan if (slc) {
489c66bbc91SGabor Kovesdan slc->sorted = sl->sorted;
490c66bbc91SGabor Kovesdan slc->start_position = sl->start_position;
491c66bbc91SGabor Kovesdan sl->start_position += slc->tosort_num;
492c66bbc91SGabor Kovesdan if (SMALL_NODE(slc))
493c66bbc91SGabor Kovesdan run_sort_level_next(slc);
494c66bbc91SGabor Kovesdan else
495c66bbc91SGabor Kovesdan push_ls(slc);
496c66bbc91SGabor Kovesdan sl->sublevels[n] = NULL;
497c66bbc91SGabor Kovesdan }
498c66bbc91SGabor Kovesdan }
499c66bbc91SGabor Kovesdan
500c66bbc91SGabor Kovesdan memcpy(sl->sorted + sl->start_position, sl->leaves,
501c66bbc91SGabor Kovesdan sl->leaves_num * sizeof(struct sort_list_item*));
502c66bbc91SGabor Kovesdan sort_left_dec(sl->leaves_num);
503c66bbc91SGabor Kovesdan }
504c66bbc91SGabor Kovesdan
505c66bbc91SGabor Kovesdan end:
506c66bbc91SGabor Kovesdan free_sort_level(sl);
507c66bbc91SGabor Kovesdan }
508c66bbc91SGabor Kovesdan
509c66bbc91SGabor Kovesdan /*
510c66bbc91SGabor Kovesdan * Single-threaded sort cycle
511c66bbc91SGabor Kovesdan */
512c66bbc91SGabor Kovesdan static void
run_sort_cycle_st(void)513c66bbc91SGabor Kovesdan run_sort_cycle_st(void)
514c66bbc91SGabor Kovesdan {
515c66bbc91SGabor Kovesdan struct sort_level *slc;
516c66bbc91SGabor Kovesdan
517c66bbc91SGabor Kovesdan for (;;) {
518c66bbc91SGabor Kovesdan slc = pop_ls_st();
519c66bbc91SGabor Kovesdan if (slc == NULL) {
520c66bbc91SGabor Kovesdan break;
521c66bbc91SGabor Kovesdan }
522c66bbc91SGabor Kovesdan run_sort_level_next(slc);
523c66bbc91SGabor Kovesdan }
524c66bbc91SGabor Kovesdan }
525c66bbc91SGabor Kovesdan
526c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
527c66bbc91SGabor Kovesdan
528c66bbc91SGabor Kovesdan /*
529c66bbc91SGabor Kovesdan * Multi-threaded sort cycle
530c66bbc91SGabor Kovesdan */
531c66bbc91SGabor Kovesdan static void
run_sort_cycle_mt(void)532c66bbc91SGabor Kovesdan run_sort_cycle_mt(void)
533c66bbc91SGabor Kovesdan {
534c66bbc91SGabor Kovesdan struct sort_level *slc;
535c66bbc91SGabor Kovesdan
536c66bbc91SGabor Kovesdan for (;;) {
537c66bbc91SGabor Kovesdan slc = pop_ls_mt();
538692cd1a3SPedro F. Giffuni if (slc == NULL)
539c66bbc91SGabor Kovesdan break;
540c66bbc91SGabor Kovesdan run_sort_level_next(slc);
541c66bbc91SGabor Kovesdan }
542c66bbc91SGabor Kovesdan }
543c66bbc91SGabor Kovesdan
544c66bbc91SGabor Kovesdan /*
545c66bbc91SGabor Kovesdan * Sort cycle thread (in multi-threaded mode)
546c66bbc91SGabor Kovesdan */
547c66bbc91SGabor Kovesdan static void*
sort_thread(void * arg)548c66bbc91SGabor Kovesdan sort_thread(void* arg)
549c66bbc91SGabor Kovesdan {
550c66bbc91SGabor Kovesdan run_sort_cycle_mt();
551c66bbc91SGabor Kovesdan sem_post(&mtsem);
552c66bbc91SGabor Kovesdan
553c66bbc91SGabor Kovesdan return (arg);
554c66bbc91SGabor Kovesdan }
555c66bbc91SGabor Kovesdan
556c66bbc91SGabor Kovesdan #endif /* defined(SORT_THREADS) */
557c66bbc91SGabor Kovesdan
558c66bbc91SGabor Kovesdan static void
run_top_sort_level(struct sort_level * sl)559c66bbc91SGabor Kovesdan run_top_sort_level(struct sort_level *sl)
560c66bbc91SGabor Kovesdan {
561c66bbc91SGabor Kovesdan struct sort_level *slc;
562c66bbc91SGabor Kovesdan
563c66bbc91SGabor Kovesdan reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag :
564c66bbc91SGabor Kovesdan default_sort_mods->rflag;
565c66bbc91SGabor Kovesdan
566c66bbc91SGabor Kovesdan sl->start_position = 0;
567c66bbc91SGabor Kovesdan sl->sln = 256;
568ecc3c291SBaptiste Daroussin sl->sublevels = sort_calloc(1, slsz);
569c66bbc91SGabor Kovesdan
570c66bbc91SGabor Kovesdan for (size_t i = 0; i < sl->tosort_num; ++i)
571c66bbc91SGabor Kovesdan place_item(sl, i);
572c66bbc91SGabor Kovesdan
573c66bbc91SGabor Kovesdan if (sl->leaves_num > 1) {
574c66bbc91SGabor Kovesdan if (keys_num > 1) {
575c66bbc91SGabor Kovesdan if (sort_opts_vals.sflag) {
576c66bbc91SGabor Kovesdan mergesort(sl->leaves, sl->leaves_num,
577c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
578c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll);
579c66bbc91SGabor Kovesdan } else {
5805d5151aeSGabor Kovesdan DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
581c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
582c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll);
583c66bbc91SGabor Kovesdan }
584c66bbc91SGabor Kovesdan } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
5855d5151aeSGabor Kovesdan DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
586c66bbc91SGabor Kovesdan sizeof(struct sort_list_item *),
587c66bbc91SGabor Kovesdan (int(*)(const void *, const void *)) list_coll_by_str_only);
588c66bbc91SGabor Kovesdan }
589c66bbc91SGabor Kovesdan }
590c66bbc91SGabor Kovesdan
591c66bbc91SGabor Kovesdan if (!reverse_sort) {
592c66bbc91SGabor Kovesdan memcpy(sl->tosort + sl->start_position, sl->leaves,
593c66bbc91SGabor Kovesdan sl->leaves_num * sizeof(struct sort_list_item*));
594c66bbc91SGabor Kovesdan sl->start_position += sl->leaves_num;
595c66bbc91SGabor Kovesdan sort_left_dec(sl->leaves_num);
596c66bbc91SGabor Kovesdan
597c66bbc91SGabor Kovesdan for (size_t i = 0; i < sl->sln; ++i) {
598c66bbc91SGabor Kovesdan slc = sl->sublevels[i];
599c66bbc91SGabor Kovesdan
600c66bbc91SGabor Kovesdan if (slc) {
601c66bbc91SGabor Kovesdan slc->sorted = sl->tosort;
602c66bbc91SGabor Kovesdan slc->start_position = sl->start_position;
603c66bbc91SGabor Kovesdan sl->start_position += slc->tosort_num;
604c66bbc91SGabor Kovesdan push_ls(slc);
605c66bbc91SGabor Kovesdan sl->sublevels[i] = NULL;
606c66bbc91SGabor Kovesdan }
607c66bbc91SGabor Kovesdan }
608c66bbc91SGabor Kovesdan
609c66bbc91SGabor Kovesdan } else {
610c66bbc91SGabor Kovesdan size_t n;
611c66bbc91SGabor Kovesdan
612c66bbc91SGabor Kovesdan for (size_t i = 0; i < sl->sln; ++i) {
613c66bbc91SGabor Kovesdan
614c66bbc91SGabor Kovesdan n = sl->sln - i - 1;
615c66bbc91SGabor Kovesdan slc = sl->sublevels[n];
616c66bbc91SGabor Kovesdan
617c66bbc91SGabor Kovesdan if (slc) {
618c66bbc91SGabor Kovesdan slc->sorted = sl->tosort;
619c66bbc91SGabor Kovesdan slc->start_position = sl->start_position;
620c66bbc91SGabor Kovesdan sl->start_position += slc->tosort_num;
621c66bbc91SGabor Kovesdan push_ls(slc);
622c66bbc91SGabor Kovesdan sl->sublevels[n] = NULL;
623c66bbc91SGabor Kovesdan }
624c66bbc91SGabor Kovesdan }
625c66bbc91SGabor Kovesdan
626c66bbc91SGabor Kovesdan memcpy(sl->tosort + sl->start_position, sl->leaves,
627c66bbc91SGabor Kovesdan sl->leaves_num * sizeof(struct sort_list_item*));
628c66bbc91SGabor Kovesdan
629c66bbc91SGabor Kovesdan sort_left_dec(sl->leaves_num);
630c66bbc91SGabor Kovesdan }
631c66bbc91SGabor Kovesdan
632c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
633c66bbc91SGabor Kovesdan if (nthreads < 2) {
634c66bbc91SGabor Kovesdan #endif
635c66bbc91SGabor Kovesdan run_sort_cycle_st();
636c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
637c66bbc91SGabor Kovesdan } else {
638c66bbc91SGabor Kovesdan size_t i;
639c66bbc91SGabor Kovesdan
640c66bbc91SGabor Kovesdan for(i = 0; i < nthreads; ++i) {
641c66bbc91SGabor Kovesdan pthread_attr_t attr;
642c66bbc91SGabor Kovesdan pthread_t pth;
643c66bbc91SGabor Kovesdan
644c66bbc91SGabor Kovesdan pthread_attr_init(&attr);
645692cd1a3SPedro F. Giffuni pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED);
646c66bbc91SGabor Kovesdan
6475ca724dcSGabor Kovesdan for (;;) {
6485ca724dcSGabor Kovesdan int res = pthread_create(&pth, &attr,
6495ca724dcSGabor Kovesdan sort_thread, NULL);
6505ca724dcSGabor Kovesdan if (res >= 0)
6515ca724dcSGabor Kovesdan break;
6525ca724dcSGabor Kovesdan if (errno == EAGAIN) {
6535ca724dcSGabor Kovesdan pthread_yield();
6545ca724dcSGabor Kovesdan continue;
6555ca724dcSGabor Kovesdan }
6565ca724dcSGabor Kovesdan err(2, NULL);
6575ca724dcSGabor Kovesdan }
658c66bbc91SGabor Kovesdan
659c66bbc91SGabor Kovesdan pthread_attr_destroy(&attr);
660c66bbc91SGabor Kovesdan }
661c66bbc91SGabor Kovesdan
662c66bbc91SGabor Kovesdan for (i = 0; i < nthreads; ++i)
663c66bbc91SGabor Kovesdan sem_wait(&mtsem);
664c66bbc91SGabor Kovesdan }
665c66bbc91SGabor Kovesdan #endif /* defined(SORT_THREADS) */
666c66bbc91SGabor Kovesdan }
667c66bbc91SGabor Kovesdan
668c66bbc91SGabor Kovesdan static void
run_sort(struct sort_list_item ** base,size_t nmemb)669c66bbc91SGabor Kovesdan run_sort(struct sort_list_item **base, size_t nmemb)
670c66bbc91SGabor Kovesdan {
671c66bbc91SGabor Kovesdan struct sort_level *sl;
672c66bbc91SGabor Kovesdan
673c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
6745ca724dcSGabor Kovesdan size_t nthreads_save = nthreads;
6755ca724dcSGabor Kovesdan if (nmemb < MT_SORT_THRESHOLD)
6765ca724dcSGabor Kovesdan nthreads = 1;
6775ca724dcSGabor Kovesdan
678c66bbc91SGabor Kovesdan if (nthreads > 1) {
679c66bbc91SGabor Kovesdan pthread_mutexattr_t mattr;
680c66bbc91SGabor Kovesdan
681c66bbc91SGabor Kovesdan pthread_mutexattr_init(&mattr);
682c66bbc91SGabor Kovesdan pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
683c66bbc91SGabor Kovesdan
684c66bbc91SGabor Kovesdan pthread_mutex_init(&g_ls_mutex, &mattr);
685692cd1a3SPedro F. Giffuni pthread_cond_init(&g_ls_cond, NULL);
686c66bbc91SGabor Kovesdan
687c66bbc91SGabor Kovesdan pthread_mutexattr_destroy(&mattr);
688c66bbc91SGabor Kovesdan
689c66bbc91SGabor Kovesdan sem_init(&mtsem, 0, 0);
690c66bbc91SGabor Kovesdan
691c66bbc91SGabor Kovesdan }
692c66bbc91SGabor Kovesdan #endif
693c66bbc91SGabor Kovesdan
694ecc3c291SBaptiste Daroussin sl = sort_calloc(1, sizeof(struct sort_level));
695c66bbc91SGabor Kovesdan
696c66bbc91SGabor Kovesdan sl->tosort = base;
697c66bbc91SGabor Kovesdan sl->tosort_num = nmemb;
698c66bbc91SGabor Kovesdan sl->tosort_sz = nmemb;
699c66bbc91SGabor Kovesdan
700c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
701c66bbc91SGabor Kovesdan sort_left = nmemb;
702c66bbc91SGabor Kovesdan #endif
703c66bbc91SGabor Kovesdan
704c66bbc91SGabor Kovesdan run_top_sort_level(sl);
705c66bbc91SGabor Kovesdan
706c66bbc91SGabor Kovesdan free_sort_level(sl);
707c66bbc91SGabor Kovesdan
708c66bbc91SGabor Kovesdan #if defined(SORT_THREADS)
709c66bbc91SGabor Kovesdan if (nthreads > 1) {
710c66bbc91SGabor Kovesdan sem_destroy(&mtsem);
711c66bbc91SGabor Kovesdan pthread_mutex_destroy(&g_ls_mutex);
712c66bbc91SGabor Kovesdan }
7135ca724dcSGabor Kovesdan nthreads = nthreads_save;
714c66bbc91SGabor Kovesdan #endif
715c66bbc91SGabor Kovesdan }
716c66bbc91SGabor Kovesdan
717c66bbc91SGabor Kovesdan void
rxsort(struct sort_list_item ** base,size_t nmemb)718c66bbc91SGabor Kovesdan rxsort(struct sort_list_item **base, size_t nmemb)
719c66bbc91SGabor Kovesdan {
720c66bbc91SGabor Kovesdan
721c66bbc91SGabor Kovesdan run_sort(base, nmemb);
722c66bbc91SGabor Kovesdan }
723