blob: 2faa5897eb2638e28422c5a79e6a099eefc0378c [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * Imported into CLIB by Eliot Dresselhaus from:
17 *
18 * This file is part of
19 * MakeIndex - A formatter and format independent index processor
20 *
21 * This file is public domain software donated by
22 * Nelson Beebe (beebe@science.utah.edu).
23 *
24 * modifications copyright (c) 2003 Cisco Systems, Inc.
25 */
26
27#include <vppinfra/clib.h>
28
29/*
30 * qsort.c: Our own version of the system qsort routine which is faster by an
31 * average of 25%, with lows and highs of 10% and 50%. The THRESHold below is
32 * the insertion sort threshold, and has been adjusted for records of size 48
33 * bytes. The MTHREShold is where we stop finding a better median.
34 */
35
Dave Barachc3799992016-08-15 11:12:27 -040036#define THRESH 4 /* threshold for insertion */
37#define MTHRESH 6 /* threshold for median */
Ed Warnickecb9cada2015-12-08 15:45:58 -070038
Dave Barachc3799992016-08-15 11:12:27 -040039typedef struct
40{
Ed Warnickecb9cada2015-12-08 15:45:58 -070041 word qsz; /* size of each record */
42 word thresh; /* THRESHold in chars */
43 word mthresh; /* MTHRESHold in chars */
Dave Barachc3799992016-08-15 11:12:27 -040044 int (*qcmp) (const void *, const void *); /* the comparison routine */
Ed Warnickecb9cada2015-12-08 15:45:58 -070045} qst_t;
46
Dave Barachc3799992016-08-15 11:12:27 -040047static void qst (qst_t * q, char *base, char *max);
Ed Warnickecb9cada2015-12-08 15:45:58 -070048
49/*
50 * qqsort: First, set up some global parameters for qst to share.
51 * Then, quicksort with qst(), and then a cleanup insertion sort ourselves.
52 * Sound simple? It's not...
53 */
54
55void
Dave Barachc3799992016-08-15 11:12:27 -040056qsort (void *base, uword n, uword size,
Ed Warnickecb9cada2015-12-08 15:45:58 -070057 int (*compar) (const void *, const void *))
58{
59 char *i;
60 char *j;
61 char *lo;
62 char *hi;
63 char *min;
64 char c;
65 char *max;
Dave Barachc3799992016-08-15 11:12:27 -040066 qst_t _q, *q = &_q;
Ed Warnickecb9cada2015-12-08 15:45:58 -070067
68 if (n <= 1)
69 return;
70
71 q->qsz = size;
72 q->qcmp = compar;
73 q->thresh = q->qsz * THRESH;
74 q->mthresh = q->qsz * MTHRESH;
75 max = base + n * q->qsz;
Dave Barachc3799992016-08-15 11:12:27 -040076 if (n >= THRESH)
77 {
78 qst (q, base, max);
79 hi = base + q->thresh;
80 }
81 else
82 {
83 hi = max;
84 }
Ed Warnickecb9cada2015-12-08 15:45:58 -070085 /*
86 * First put smallest element, which must be in the first THRESH, in the
87 * first position as a sentinel. This is done just by searching the
88 * first THRESH elements (or the first n if n < THRESH), finding the min,
89 * and swapping it into the first position.
90 */
Dave Barachc3799992016-08-15 11:12:27 -040091 for (j = lo = base; (lo += q->qsz) < hi;)
92 {
93 if ((*compar) (j, lo) > 0)
94 j = lo;
Ed Warnickecb9cada2015-12-08 15:45:58 -070095 }
Dave Barachc3799992016-08-15 11:12:27 -040096 if (j != base)
97 { /* swap j into place */
98 for (i = base, hi = base + q->qsz; i < hi;)
99 {
100 c = *j;
101 *j++ = *i;
102 *i++ = c;
103 }
104 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105 /*
106 * With our sentinel in place, we now run the following hyper-fast
107 * insertion sort. For each remaining element, min, from [1] to [n-1],
108 * set hi to the index of the element AFTER which this one goes. Then, do
109 * the standard insertion sort shift on a character at a time basis for
110 * each element in the frob.
111 */
Dave Barachc3799992016-08-15 11:12:27 -0400112 for (min = base; (hi = min += q->qsz) < max;)
113 {
114 while ((*q->qcmp) (hi -= q->qsz, min) > 0);
115 if ((hi += q->qsz) != min)
116 {
117 for (lo = min + q->qsz; --lo >= min;)
118 {
119 c = *lo;
120 for (i = j = lo; (j -= q->qsz) >= hi; i = j)
121 *i = *j;
122 *i = c;
123 }
124 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700125 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700126}
127
128
129
130/*
131 * qst: Do a quicksort. First, find the median element, and put that one in
132 * the first place as the discriminator. (This "median" is just the median
133 * of the first, last and middle elements). (Using this median instead of
134 * the first element is a big win). Then, the usual partitioning/swapping,
135 * followed by moving the discriminator into the right place. Then, figure
136 * out the sizes of the two partions, do the smaller one recursively and the
137 * larger one via a repeat of this code. Stopping when there are less than
138 * THRESH elements in a partition and cleaning up with an insertion sort (in
139 * our caller) is a huge win. All data swaps are done in-line, which is
140 * space-losing but time-saving. (And there are only three places where this
141 * is done).
142 */
143
144static void
Dave Barachc3799992016-08-15 11:12:27 -0400145qst (qst_t * q, char *base, char *max)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700146{
147 char *i;
148 char *j;
149 char *jj;
150 char *mid;
151 int ii;
152 char c;
Dave Barachc3799992016-08-15 11:12:27 -0400153 char *tmp;
154 int lo;
155 int hi;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700156 int qsz = q->qsz;
157
Dave Barachc3799992016-08-15 11:12:27 -0400158 lo = (int) (max - base); /* number of elements as chars */
159 do
160 {
161 /*
162 * At the top here, lo is the number of characters of elements in the
163 * current partition. (Which should be max - base). Find the median
164 * of the first, last, and middle element and make that the middle
165 * element. Set j to largest of first and middle. If max is larger
166 * than that guy, then it's that guy, else compare max with loser of
167 * first and take larger. Things are set up to prefer the middle,
168 * then the first in case of ties.
169 */
170 mid = i = base + qsz * ((unsigned) (lo / qsz) >> 1);
171 if (lo >= q->mthresh)
172 {
173 j = ((*q->qcmp) ((jj = base), i) > 0 ? jj : i);
174 if ((*q->qcmp) (j, (tmp = max - qsz)) > 0)
175 {
176 /* switch to first loser */
177 j = (j == jj ? i : jj);
178 if ((*q->qcmp) (j, tmp) < 0)
179 j = tmp;
180 }
181 if (j != i)
182 {
183 ii = qsz;
184 do
185 {
186 c = *i;
187 *i++ = *j;
188 *j++ = c;
189 }
190 while (--ii);
191 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700192 }
Dave Barachc3799992016-08-15 11:12:27 -0400193 /* Semi-standard quicksort partitioning/swapping */
194 for (i = base, j = max - qsz;;)
195 {
196 while (i < mid && (*q->qcmp) (i, mid) <= 0)
197 i += qsz;
198 while (j > mid)
199 {
200 if ((*q->qcmp) (mid, j) <= 0)
201 {
202 j -= qsz;
203 continue;
204 }
205 tmp = i + qsz; /* value of i after swap */
206 if (i == mid)
207 { /* j <-> mid, new mid is j */
208 mid = jj = j;
209 }
210 else
211 { /* i <-> j */
212 jj = j;
213 j -= qsz;
214 }
215 goto swap;
216 }
217 if (i == mid)
218 {
219 break;
220 }
221 else
222 { /* i <-> mid, new mid is i */
223 jj = mid;
224 tmp = mid = i; /* value of i after swap */
225 j -= qsz;
226 }
227 swap:
228 ii = qsz;
229 do
230 {
231 c = *i;
232 *i++ = *jj;
233 *jj++ = c;
234 }
235 while (--ii);
236 i = tmp;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700237 }
Dave Barachc3799992016-08-15 11:12:27 -0400238 /*
239 * Look at sizes of the two partitions, do the smaller one first by
240 * recursion, then do the larger one by making sure lo is its size,
241 * base and max are update correctly, and branching back. But only
242 * repeat (recursively or by branching) if the partition is of at
243 * least size THRESH.
244 */
245 i = (j = mid) + qsz;
246 if ((lo = (int) (j - base)) <= (hi = (int) (max - i)))
247 {
248 if (lo >= q->thresh)
249 qst (q, base, j);
250 base = i;
251 lo = hi;
252 }
253 else
254 {
255 if (hi >= q->thresh)
256 qst (q, i, max);
257 max = j;
258 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700259 }
Dave Barachc3799992016-08-15 11:12:27 -0400260 while (lo >= q->thresh);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700261}
Dave Barachc3799992016-08-15 11:12:27 -0400262
263/*
264 * fd.io coding-style-patch-verification: ON
265 *
266 * Local Variables:
267 * eval: (c-set-style "gnu")
268 * End:
269 */