2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
5 * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
35 #if defined(SORT_THREADS)
37 #include <semaphore.h>
46 #include "radixsort.h"
48 #define DEFAULT_SORT_FUNC_RADIXSORT mergesort
50 #define TINY_NODE(sl) ((sl)->tosort_num < 65)
51 #define SMALL_NODE(sl) ((sl)->tosort_num < 5)
53 /* are we sorting in reverse order ? */
54 static bool reverse_sort;
56 /* sort sub-levels array size */
57 static const size_t slsz = 256 * sizeof(struct sort_level*);
59 /* one sort level structure */
62 struct sort_level **sublevels;
63 struct sort_list_item **leaves;
64 struct sort_list_item **sorted;
65 struct sort_list_item **tosort;
70 size_t start_position;
76 /* stack of sort levels ready to be sorted */
78 struct level_stack *next;
79 struct sort_level *sl;
82 static struct level_stack *g_ls;
84 #if defined(SORT_THREADS)
85 /* stack guarding mutex */
86 static pthread_cond_t g_ls_cond;
87 static pthread_mutex_t g_ls_mutex;
89 /* counter: how many items are left */
90 static size_t sort_left;
93 /* semaphore to count threads */
97 * Decrement items counter
100 sort_left_dec(size_t n)
102 pthread_mutex_lock(&g_ls_mutex);
104 if (sort_left == 0 && nthreads > 1)
105 pthread_cond_broadcast(&g_ls_cond);
106 pthread_mutex_unlock(&g_ls_mutex);
110 * Do we have something to sort ?
112 * This routine does not need to be locked.
119 ret = (sort_left > 0);
126 #define sort_left_dec(n)
128 #endif /* SORT_THREADS */
131 _push_ls(struct level_stack *ls)
139 * Push sort level to the stack
142 push_ls(struct sort_level *sl)
144 struct level_stack *new_ls;
146 new_ls = sort_malloc(sizeof(struct level_stack));
149 #if defined(SORT_THREADS)
151 pthread_mutex_lock(&g_ls_mutex);
153 pthread_cond_signal(&g_ls_cond);
154 pthread_mutex_unlock(&g_ls_mutex);
161 * Pop sort level from the stack (single-threaded style)
163 static inline struct sort_level*
166 struct sort_level *sl;
169 struct level_stack *saved_ls;
181 #if defined(SORT_THREADS)
184 * Pop sort level from the stack (multi-threaded style)
186 static inline struct sort_level*
189 struct level_stack *saved_ls;
190 struct sort_level *sl;
192 pthread_mutex_lock(&g_ls_mutex);
204 if (have_sort_left() == 0)
206 pthread_cond_wait(&g_ls_cond, &g_ls_mutex);
209 pthread_mutex_unlock(&g_ls_mutex);
216 #endif /* defined(SORT_THREADS) */
219 add_to_sublevel(struct sort_level *sl, struct sort_list_item *item, size_t indx)
221 struct sort_level *ssl;
223 ssl = sl->sublevels[indx];
226 ssl = sort_calloc(1, sizeof(struct sort_level));
228 ssl->level = sl->level + 1;
229 sl->sublevels[indx] = ssl;
234 if (++(ssl->tosort_num) > ssl->tosort_sz) {
235 ssl->tosort_sz = ssl->tosort_num + 128;
236 ssl->tosort = sort_realloc(ssl->tosort,
237 sizeof(struct sort_list_item*) * (ssl->tosort_sz));
240 ssl->tosort[ssl->tosort_num - 1] = item;
244 add_leaf(struct sort_level *sl, struct sort_list_item *item)
247 if (++(sl->leaves_num) > sl->leaves_sz) {
248 sl->leaves_sz = sl->leaves_num + 128;
249 sl->leaves = sort_realloc(sl->leaves,
250 (sizeof(struct sort_list_item*) * (sl->leaves_sz)));
252 sl->leaves[sl->leaves_num - 1] = item;
256 get_wc_index(struct sort_list_item *sli, size_t level)
258 const size_t wcfact = (mb_cur_max == 1) ? 1 : sizeof(wchar_t);
259 const struct key_value *kv;
260 const struct bwstring *bws;
262 kv = get_key_from_keys_array(&sli->ka, 0);
265 if ((BWSLEN(bws) * wcfact > level)) {
269 * Sort wchar strings a byte at a time, rather than a single
270 * byte from each wchar.
272 res = (wchar_t)BWS_GET(bws, level / wcfact);
273 /* Sort most-significant byte first. */
274 if (level % wcfact < wcfact - 1)
275 res = (res >> (8 * (wcfact - 1 - (level % wcfact))));
284 place_item(struct sort_level *sl, size_t item)
286 struct sort_list_item *sli;
289 sli = sl->tosort[item];
290 c = get_wc_index(sli, sl->level);
295 add_to_sublevel(sl, sli, c);
299 free_sort_level(struct sort_level *sl)
304 sort_free(sl->leaves);
307 sort_free(sl->tosort);
310 struct sort_level *slc;
315 for (size_t i = 0; i < sln; ++i) {
316 slc = sl->sublevels[i];
318 free_sort_level(slc);
321 sort_free(sl->sublevels);
329 run_sort_level_next(struct sort_level *sl)
331 const size_t wcfact = (mb_cur_max == 1) ? 1 : sizeof(wchar_t);
332 struct sort_level *slc;
333 size_t i, sln, tosort_num;
336 sort_free(sl->sublevels);
337 sl->sublevels = NULL;
340 switch (sl->tosort_num) {
344 sl->sorted[sl->start_position] = sl->tosort[0];
349 * Radixsort only processes a single byte at a time. In wchar
350 * mode, this can be a subset of the length of a character.
351 * list_coll_offset() offset is in units of wchar, not bytes.
352 * So to calculate the offset, we must divide by
353 * sizeof(wchar_t) and round down to the index of the first
354 * character this level references.
356 if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]),
357 sl->level / wcfact) > 0) {
358 sl->sorted[sl->start_position++] = sl->tosort[1];
359 sl->sorted[sl->start_position] = sl->tosort[0];
361 sl->sorted[sl->start_position++] = sl->tosort[0];
362 sl->sorted[sl->start_position] = sl->tosort[1];
368 if (TINY_NODE(sl) || (sl->level > 15)) {
372 * Collate comparison offset is in units of
373 * character-width, so we must divide the level (bytes)
374 * by operating character width (wchar_t or char). See
375 * longer comment above.
377 func = get_list_call_func(sl->level / wcfact);
379 sl->leaves = sl->tosort;
380 sl->leaves_num = sl->tosort_num;
381 sl->leaves_sz = sl->leaves_num;
382 sl->leaves = sort_realloc(sl->leaves,
383 (sizeof(struct sort_list_item *) *
390 if (sort_opts_vals.sflag) {
391 if (mergesort(sl->leaves, sl->leaves_num,
392 sizeof(struct sort_list_item *),
393 (int(*)(const void *, const void *)) func) == -1)
395 err(2, "Radix sort error 3");
397 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
398 sizeof(struct sort_list_item *),
399 (int(*)(const void *, const void *)) func);
401 memcpy(sl->sorted + sl->start_position,
402 sl->leaves, sl->leaves_num *
403 sizeof(struct sort_list_item*));
405 sort_left_dec(sl->leaves_num);
409 sl->tosort_sz = sl->tosort_num;
410 sl->tosort = sort_realloc(sl->tosort,
411 sizeof(struct sort_list_item*) * (sl->tosort_sz));
416 sl->sublevels = sort_calloc(1, slsz);
420 tosort_num = sl->tosort_num;
421 for (i = 0; i < tosort_num; ++i)
424 sort_free(sl->tosort);
429 if (sl->leaves_num > 1) {
431 if (sort_opts_vals.sflag) {
432 mergesort(sl->leaves, sl->leaves_num,
433 sizeof(struct sort_list_item *),
434 (int(*)(const void *, const void *)) list_coll);
436 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
437 sizeof(struct sort_list_item *),
438 (int(*)(const void *, const void *)) list_coll);
440 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
441 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
442 sizeof(struct sort_list_item *),
443 (int(*)(const void *, const void *)) list_coll_by_str_only);
447 sl->leaves_sz = sl->leaves_num;
448 sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) *
452 memcpy(sl->sorted + sl->start_position, sl->leaves,
453 sl->leaves_num * sizeof(struct sort_list_item*));
454 sl->start_position += sl->leaves_num;
455 sort_left_dec(sl->leaves_num);
457 sort_free(sl->leaves);
464 for (i = 0; i < sln; ++i) {
465 slc = sl->sublevels[i];
468 slc->sorted = sl->sorted;
469 slc->start_position = sl->start_position;
470 sl->start_position += slc->tosort_num;
472 run_sort_level_next(slc);
475 sl->sublevels[i] = NULL;
484 for (i = 0; i < sln; ++i) {
486 slc = sl->sublevels[n];
489 slc->sorted = sl->sorted;
490 slc->start_position = sl->start_position;
491 sl->start_position += slc->tosort_num;
493 run_sort_level_next(slc);
496 sl->sublevels[n] = NULL;
500 memcpy(sl->sorted + sl->start_position, sl->leaves,
501 sl->leaves_num * sizeof(struct sort_list_item*));
502 sort_left_dec(sl->leaves_num);
510 * Single-threaded sort cycle
513 run_sort_cycle_st(void)
515 struct sort_level *slc;
522 run_sort_level_next(slc);
526 #if defined(SORT_THREADS)
529 * Multi-threaded sort cycle
532 run_sort_cycle_mt(void)
534 struct sort_level *slc;
540 run_sort_level_next(slc);
545 * Sort cycle thread (in multi-threaded mode)
548 sort_thread(void* arg)
556 #endif /* defined(SORT_THREADS) */
559 run_top_sort_level(struct sort_level *sl)
561 struct sort_level *slc;
563 reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag :
564 default_sort_mods->rflag;
566 sl->start_position = 0;
568 sl->sublevels = sort_calloc(1, slsz);
570 for (size_t i = 0; i < sl->tosort_num; ++i)
573 if (sl->leaves_num > 1) {
575 if (sort_opts_vals.sflag) {
576 mergesort(sl->leaves, sl->leaves_num,
577 sizeof(struct sort_list_item *),
578 (int(*)(const void *, const void *)) list_coll);
580 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
581 sizeof(struct sort_list_item *),
582 (int(*)(const void *, const void *)) list_coll);
584 } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
585 DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
586 sizeof(struct sort_list_item *),
587 (int(*)(const void *, const void *)) list_coll_by_str_only);
592 memcpy(sl->tosort + sl->start_position, sl->leaves,
593 sl->leaves_num * sizeof(struct sort_list_item*));
594 sl->start_position += sl->leaves_num;
595 sort_left_dec(sl->leaves_num);
597 for (size_t i = 0; i < sl->sln; ++i) {
598 slc = sl->sublevels[i];
601 slc->sorted = sl->tosort;
602 slc->start_position = sl->start_position;
603 sl->start_position += slc->tosort_num;
605 sl->sublevels[i] = NULL;
612 for (size_t i = 0; i < sl->sln; ++i) {
615 slc = sl->sublevels[n];
618 slc->sorted = sl->tosort;
619 slc->start_position = sl->start_position;
620 sl->start_position += slc->tosort_num;
622 sl->sublevels[n] = NULL;
626 memcpy(sl->tosort + sl->start_position, sl->leaves,
627 sl->leaves_num * sizeof(struct sort_list_item*));
629 sort_left_dec(sl->leaves_num);
632 #if defined(SORT_THREADS)
636 #if defined(SORT_THREADS)
640 for(i = 0; i < nthreads; ++i) {
644 pthread_attr_init(&attr);
645 pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED);
648 int res = pthread_create(&pth, &attr,
652 if (errno == EAGAIN) {
659 pthread_attr_destroy(&attr);
662 for (i = 0; i < nthreads; ++i)
665 #endif /* defined(SORT_THREADS) */
669 run_sort(struct sort_list_item **base, size_t nmemb)
671 struct sort_level *sl;
673 #if defined(SORT_THREADS)
674 size_t nthreads_save = nthreads;
675 if (nmemb < MT_SORT_THRESHOLD)
679 pthread_mutexattr_t mattr;
681 pthread_mutexattr_init(&mattr);
682 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
684 pthread_mutex_init(&g_ls_mutex, &mattr);
685 pthread_cond_init(&g_ls_cond, NULL);
687 pthread_mutexattr_destroy(&mattr);
689 sem_init(&mtsem, 0, 0);
694 sl = sort_calloc(1, sizeof(struct sort_level));
697 sl->tosort_num = nmemb;
698 sl->tosort_sz = nmemb;
700 #if defined(SORT_THREADS)
704 run_top_sort_level(sl);
708 #if defined(SORT_THREADS)
711 pthread_mutex_destroy(&g_ls_mutex);
713 nthreads = nthreads_save;
718 rxsort(struct sort_list_item **base, size_t nmemb)
721 run_sort(base, nmemb);