Subversion Repositories Kolibri OS

Rev

Rev 9013 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
9013 turbocat 1
/* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
2
#include 
3
 
4
/*-
5
 * Copyright (c) 1980, 1983 The Regents of the University of California.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms are permitted
9
 * provided that: (1) source distributions retain this entire copyright
10
 * notice and comment, and (2) distributions including binaries display
11
 * the following acknowledgement:  ``This product includes software
12
 * developed by the University of California, Berkeley and its contributors''
13
 * in the documentation or other materials provided with the distribution
14
 * and in all advertising materials mentioning features or use of this
15
 * software. Neither the name of the University nor the names of its
16
 * contributors may be used to endorse or promote products derived
17
 * from this software without specific prior written permission.
18
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21
 */
22
 
23
/*
24
 * qsort.c:
25
 * Our own version of the system qsort routine which is faster by an average
26
 * of 25%, with lows and highs of 10% and 50%.
27
 * The THRESHold below is the insertion sort threshold, and has been adjusted
28
 * for records of size 48 bytes.
29
 * The MTHREShold is where we stop finding a better median.
30
 */
31
 
9765 turbocat 32
#define THRESH 4 /* threshold for insertion */
33
#define MTHRESH 6 /* threshold for median */
9013 turbocat 34
 
9765 turbocat 35
static int (*qcmp)(const void*, const void*); /* the comparison routine */
36
static int qsz; /* size of each record */
37
static int thresh; /* THRESHold in chars */
38
static int mthresh; /* MTHRESHold in chars */
9013 turbocat 39
 
40
/*
41
 * qst:
42
 * Do a quicksort
43
 * First, find the median element, and put that one in the first place as the
44
 * discriminator.  (This "median" is just the median of the first, last and
45
 * middle elements).  (Using this median instead of the first element is a big
46
 * win).  Then, the usual partitioning/swapping, followed by moving the
47
 * discriminator into the right place.  Then, figure out the sizes of the two
48
 * partions, do the smaller one recursively and the larger one via a repeat of
49
 * this code.  Stopping when there are less than THRESH elements in a partition
50
 * and cleaning up with an insertion sort (in our caller) is a huge win.
51
 * All data swaps are done in-line, which is space-losing but time-saving.
52
 * (And there are only three places where this is done).
53
 */
54
 
9765 turbocat 55
static void qst(char* base, char* max)
9013 turbocat 56
{
9765 turbocat 57
    char c, *i, *j, *jj;
58
    int ii;
59
    char *mid, *tmp;
60
    int lo, hi;
9013 turbocat 61
 
62
    /*
9765 turbocat 63
     * At the top here, lo is the number of characters of elements in the
64
     * current partition.  (Which should be max - base).
65
     * Find the median of the first, last, and middle element and make
66
     * that the middle element.  Set j to largest of first and middle.
67
     * If max is larger than that guy, then it's that guy, else compare
68
     * max with loser of first and take larger.  Things are set up to
69
     * prefer the middle, then the first in case of ties.
9013 turbocat 70
     */
9765 turbocat 71
    lo = max - base; /* number of elements as chars */
72
    do {
73
        mid = i = base + qsz * ((lo / qsz) >> 1);
74
        if (lo >= mthresh) {
75
            j = (qcmp((jj = base), i) > 0 ? jj : i);
76
            if (qcmp(j, (tmp = max - qsz)) > 0) {
77
                /* switch to first loser */
78
                j = (j == jj ? i : jj);
79
                if (qcmp(j, tmp) < 0)
80
                    j = tmp;
81
            }
82
            if (j != i) {
83
                ii = qsz;
84
                do {
85
                    c = *i;
86
                    *i++ = *j;
87
                    *j++ = c;
88
                } while (--ii);
89
            }
90
        }
91
        /*
92
         * Semi-standard quicksort partitioning/swapping
93
         */
94
        for (i = base, j = max - qsz;;) {
95
            while (i < mid && qcmp(i, mid) <= 0)
96
                i += qsz;
97
            while (j > mid) {
98
                if (qcmp(mid, j) <= 0) {
99
                    j -= qsz;
100
                    continue;
101
                }
102
                tmp = i + qsz; /* value of i after swap */
103
                if (i == mid) {
104
                    /* j <-> mid, new mid is j */
105
                    mid = jj = j;
106
                } else {
107
                    /* i <-> j */
108
                    jj = j;
109
                    j -= qsz;
110
                }
111
                goto swap;
112
            }
113
            if (i == mid) {
114
                break;
115
            } else {
116
                /* i <-> mid, new mid is i */
117
                jj = mid;
118
                tmp = mid = i; /* value of i after swap */
119
                j -= qsz;
120
            }
121
        swap:
122
            ii = qsz;
123
            do {
124
                c = *i;
125
                *i++ = *jj;
126
                *jj++ = c;
127
            } while (--ii);
128
            i = tmp;
129
        }
130
        /*
131
         * Look at sizes of the two partitions, do the smaller
132
         * one first by recursion, then do the larger one by
133
         * making sure lo is its size, base and max are update
134
         * correctly, and branching back.  But only repeat
135
         * (recursively or by branching) if the partition is
136
         * of at least size THRESH.
137
         */
138
        i = (j = mid) + qsz;
139
        if ((lo = j - base) <= (hi = max - i)) {
140
            if (lo >= thresh)
141
                qst(base, j);
142
            base = i;
143
            lo = hi;
144
        } else {
145
            if (hi >= thresh)
146
                qst(i, max);
147
            max = j;
148
        }
149
    } while (lo >= thresh);
9013 turbocat 150
}
151
 
152
/*
153
 * qsort:
154
 * First, set up some global parameters for qst to share.  Then, quicksort
155
 * with qst(), and then a cleanup insertion sort ourselves.  Sound simple?
156
 * It's not...
157
 */
158
 
9765 turbocat 159
void qsort(void* base0, size_t n, size_t size, int (*compar)(const void*, const void*))
9013 turbocat 160
{
9765 turbocat 161
    char* base = (char*)base0;
162
    char c, *i, *j, *lo, *hi;
163
    char *min, *max;
9013 turbocat 164
 
9765 turbocat 165
    if (n <= 1)
166
        return;
167
    qsz = size;
168
    qcmp = compar;
169
    thresh = qsz * THRESH;
170
    mthresh = qsz * MTHRESH;
171
    max = base + n * qsz;
172
    if (n >= THRESH) {
173
        qst(base, max);
174
        hi = base + thresh;
175
    } else {
176
        hi = max;
9013 turbocat 177
    }
9765 turbocat 178
    /*
179
     * First put smallest element, which must be in the first THRESH, in
180
     * the first position as a sentinel.  This is done just by searching
181
     * the first THRESH elements (or the first n if n < THRESH), finding
182
     * the min, and swapping it into the first position.
183
     */
184
    for (j = lo = base; (lo += qsz) < hi;)
185
        if (qcmp(j, lo) > 0)
186
            j = lo;
187
    if (j != base) {
188
        /* swap j into place */
189
        for (i = base, hi = base + qsz; i < hi;) {
190
            c = *j;
191
            *j++ = *i;
192
            *i++ = c;
193
        }
9013 turbocat 194
    }
9765 turbocat 195
    /*
196
     * With our sentinel in place, we now run the following hyper-fast
197
     * insertion sort.  For each remaining element, min, from [1] to [n-1],
198
     * set hi to the index of the element AFTER which this one goes.
199
     * Then, do the standard insertion sort shift on a character at a time
200
     * basis for each element in the frob.
201
     */
202
    for (min = base; (hi = min += qsz) < max;) {
203
        while (qcmp(hi -= qsz, min) > 0)
204
            /* void */;
205
        if ((hi += qsz) != min) {
206
            for (lo = min + qsz; --lo >= min;) {
207
                c = *lo;
208
                for (i = j = lo; (j -= qsz) >= hi; i = j)
209
                    *i = *j;
210
                *i = c;
211
            }
212
        }
213
    }
9013 turbocat 214
}